数组类算法【leetcode】

news/2024/11/8 19:57:14 标签: 算法

704. 二分查找

2024.11.06

题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。力扣题目链接

二分查找 用于有序数组中,没有重复的数组。首先需要定义一个头(left)和尾(right),最后通过mid和target对比不断修改头和尾指向的位置。

【需要注意区间问题,根据区间的开和闭决定边界条件】

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        while(left <= right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] > target)
            {
                right = mid - 1;
            }
            else if(nums[mid] == target)
            {
                return mid;
            }
            else if(nums[mid] < target)
            {
                left = mid + 1;
            }
        }
        return -1;

    }
};

推荐练习 leetcode35. 

27.移除元素

2024.11.07

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。力扣题目链接

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

这里其实有序无序都可以,正常的调用内置函数的复杂度是O(n),是用的两个for循环进行暴力解。这里要求我们原地执行,所以用一个for循环解决。

【这里我们要知道数组的删除,其实和我们平时理解的删除并不一样,数组的删除前其实是将不需要的元素进行覆盖,内存中他还是存在的】

这里用到了快慢指针,一个fast和一个slow,fast用于遍历整个数组,所以for循环的执行条件就应该是当fast小于数组长度的时候就进行一次循环,而slow指针怎么变化呢?如果nums[fast]不是需要的val,那就令nums[fast] = nums[slow],当nums[fast] = val的时候,slow指针的位置不需要变,这样可以把slow看作一个新的数组,用于装不包含val值的数组,当nums[fast]再次不等于val的时候将nums[fast]再次赋给nums[slow],slow指针再次向后移动。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow = 0;
        int fast = 0;
        for(; fast < nums.size(); fast++)
        {
            if(val != nums[fast])
            {
            nums[slow] = nums[fast];
            slow++;
            }
        }
        return slow;
    }
};

977.有序数组的平方

2024.11.07

题目:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。力扣题目链接

思路和上一题类似,也是用双指针,这里两头的平方一定会大于中间的值的平方,所以新建立一个数组从后往前放置原数组的平方的值。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int left = 0;
        int right = nums.size() - 1;
        int k = nums.size() - 1;   //因为数组的两头的数字的绝对值一定大于中间的,所以result数组从后往前放
        vector<int> result(nums.size(), 0);
        for(; left <= right;)
        {
            if(nums[left] * nums[left] < nums[right] * nums[right])
            {   
                result[k--] = nums[right] * nums[right];
                right --;
            }
            else
            {
                result[k--] = nums[left] * nums[left];
                left ++;
            }
        }
        return result;
    }
};

209.长度最小的子数组

2024.11.08

这是第一道中等题,但其实也挺简单的力扣题目链接

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

1.暴力解

这道题目暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是O(n^2)。

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX; // 最终的结果
        int sum = 0; // 子序列的数值之和
        int subLength = 0; // 子序列的长度
        for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
            sum = 0;
            for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
                sum += nums[j];
                if (sum >= s) { // 一旦发现子序列和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;
                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

2.最优解

滑动窗口,有点类似之前的双指针的解法,不断更新子序列的两头指针,只要窗口中的sum大于target,就把左边的指针往右移一格,如果sum一直小于target就右边的指针持续后移。那么我们就可以通过记录每次得到的小于sum的子数组,并每次进行比较,及时更新最小值。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0;
        int right = 0;
        int sum = 0;
        int length = 0;                     //滑动窗口的长度(子数组)

        //在算法中,当你需要找到一组数中的最小值时,可以将初始值设为 INT32_MAX,这样任何实际的数值都会比它小。
        int result = INT32_MAX;

        for(right=0;right < nums.size();right++)
        {
            sum += nums[right];
            while(sum >= target)
            {
                length = right - left + 1;  //截至sum 小于 target, 数组的长度(未必是最小的)
                if (result > length)
                {
                    result = length;        // 更新最小子数组长度
                }
                sum -= nums[left++];
            }
        }
        if(result == INT32_MAX)
            return 0;
        else
            return result;
    }
};

 不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。

 59.螺旋矩阵II

2024.11.08

这是第一道中等题,思路很简单,只需要注意边界条件,按顺序赋值就完了

题目:给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

思路:

模拟顺时针画矩阵的过程:

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

由外向内一圈一圈这么画下去.......【我按照左闭右开的原则】统一填充方式可以很大程度避免自己写到后面乱掉了。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
        int x = 0;
        int y = 0;
        int loop = n / 2;       //  循环次数
        int offset = 1;         // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
        int mid = n / 2;        //  n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
        int count = 1;          //  用来给矩阵中每一个空格赋值
        int i, j;
        while(loop--)
        {
            i = x; 
            j = y;

            for(;j < n - offset;j++)
            {
                res[i][j] = count++;
            }
            //第一个for循环完之后,j = n - offset

            for(;i < n - offset;i++)
            {
                res[i][j] = count++;
            }
            //第二个for循环完之后,i = n - offset

            for(;j > y;j--)
            {
                res[i][j] = count++;
            }

            for(;i > x;i--)
            {
                res[i][j] = count++;
            }

 // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
            x++;
            y++;

 // offset 控制每一圈里每一条边遍历的长度
            offset++;
        }
            if (n % 2) 
            {
                res[mid][mid] = count;
            }
        return res;
    }
};

未完待续.......


http://www.niftyadmin.cn/n/5744369.html

相关文章

实现 Nuxt3 预览PDF文件

安装必要的库&#xff0c;这里使用PDF.js库 npm install pdfjs-dist --save 为了解决跨域问题&#xff0c;在server/api 下 创建一个请求api&#xff0c; downloadFileByProxy.ts import { defineEventHandler } from h3;export default defineEventHandler(async event >…

前端Web用户 token 持久化

用户 token 持久化 业务背景&#xff1a;Token的有效期会持续一段时间&#xff0c;在这段时间内没有必要重复请求token&#xff0c;但是pinia本身是基于内存的管理方式&#xff0c;刷新浏览器Token会丢失&#xff0c;为了避免丢失需要配置持久化进行缓存 基础思路&#xff1a…

【数据结构】构造函数和析构函数

在一个宁静的小镇上&#xff0c;有一座神奇的玩具工厂。这个工厂每天都能制造出各种有趣的玩具。玩具工厂有两个重要的角色&#xff1a;一位是“玩具制造师”&#xff0c;另一位是“玩具清理师”。他们的工作就像我们在编程中使用的构造函数和析构函数。 ### 玩具制造师&#…

基于Springboot的学生宿舍管理系统的设计与实现-计算机毕设 附源码 26991

基于Springboot的学生宿舍管理系统的设计与实现 摘 要 学生宿舍管理系统在高校管理中具有重要的作用&#xff0c;为提高宿舍管理效率和服务质量&#xff0c;本文基于Springboot框架开发了一款学生宿舍管理系统。该系统主要分为管理员、学生用户和宿管用户三类角色&#xff0c;每…

计算机网络——SDN

分布式控制路由 集中式控制路由

搭子小程序定制开发:全新找搭子之旅

在快节奏生活下&#xff0c;年轻人的社交渠道逐渐减少&#xff0c;为了能够获得志同道合的好友&#xff0c;满足社交需求&#xff0c;找搭子成为了当下年轻人的社交潮流&#xff0c;不管出去旅游、拍照、吃饭、打游戏等都可以寻求搭子&#xff0c;获得更高的情绪价值体验。 随…

Risc-v:mhartid寄存器

简介 mhartid&#xff08;Machine Hart ID Register&#xff09;是 RISC-V 架构中的一个控制和状态寄存器&#xff08;CSR&#xff09;&#xff0c;用于存储当前硬件线程&#xff08;hart&#xff09;的标识符。 在多核处理器中&#xff0c;每个核心可能有一个或多个硬件线程&a…

pip install pynini 失败

pip install pynini 失败&#xff0c;主要错误在编译 wheel 的时候。 解决方法&#xff1a; pip install --only-binary :all: pynini 参考&#xff1a;https://github.com/kylebgorman/pynini/issues/80