代码随想录-数组部分

一.二分查找

力扣题目链接

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

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9     
输出: 4       
解释: 9 出现在 nums 中并且下标为 4     

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2     
输出: -1        
解释: 2 不存在 nums 中因此返回 -1        

提示:

    • 你可以假设 nums 中的所有元素是不重复的。
    • n 将在 [1, 10000]之间。
    • nums 的每个元素都将在 [-9999, 9999]之间。

二分查找代码模板

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

思路就是分而治之,每次将问题规模缩小为原来的一半,时间复杂度为log n

LeetCode代码

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

二.移除元素

力扣题目链接(opens new window)

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

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

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

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

你不需要考虑数组中超出新长度后面的元素。

思路

这里一开始的思路的使用快排的双指针,设置i指向队头和j指向队尾,如果a[i] = val 并且a[j] !=  val,就交换两个的值,这样就做到了把所有的val放到了后面,但是在处理边界的时候出现了问题,没有写出正确的代码

第二种思路,是双指针都指向队头,并且进行重复赋值,a[i] = a[j],如果当前检测到的不是val,就正常赋值,如果是val,就跳过,这里的方法非常巧妙,这里借鉴一下carl哥的图片

这样的核心代码就是

for(int j = 0;j < len;j++)
{
   if(a[j] != val)
   {
      a[i++] = a[j];    
    }        
}

LeetCode代码

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int j = 0;
        for(int i = 0;i < nums.size();i++)
        {
            if (nums[i] != val)
            {
                nums[j++] = nums[i];
            }
        }
     //最后返回去除了val的数组的长度
return j; } };

 

三.有序数组的平方

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

示例 1: 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]

示例 2: 输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]

思路

由于计算的是平方,所以我们无法确定处最小值,但是可以先确定出最大值,这里最大值肯定是从第一个数的平方和最后一个数的平方中选出,我们可以先找出最大值,然后从小到大存放结果,将最大值放到数组最后,然后依次寻找数组剩余中的最大值

这里因为要比较的是后面的元素平方和前面的元素平方,所以也用到了双指针

还是借用一下carl哥的图片,解释的非常直观

 核心代码

int i = 0,j = len-1;
k = len - 1;
while (i < j)
{
  if (a[i]*a[i] < a[j]*a[j])
   {
       b[k--] = a[j] * a[j];
   }  
  else
  {
       b[k--] = a[i]*a[i];  
  }            
}

LeetCode代码

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int k = nums.size() - 1;
        vector<int> res(nums.size(),0);
        int i = 0,j = nums.size() - 1;
        while (i <= j)
        {
            if (nums[i]*nums[i] < nums[j]*nums[j])
            {
                res[k--] = nums[j]*nums[j];
                j--;
            }
            else
            {
                res[k--] = nums[i]*nums[i];
                i++;
            }
        }
        return res;
    }
};

 四.长度最小的子数组

力扣题目链接(opens new window)

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

示例:

输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

提示:

  • 1 <= target <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

思路

暴力解法

两层循环,暴力破解

时间复杂度为O(n^2)

代码

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int res = INT32_MAX;
        int sum = 0;
        int subLength = 0;
        for(int i = 0;i < nums.size();i++)
        {
            sum = 0;
            for(int j = i;j < nums.size();j++)
            {
                sum += nums[j];
                if (sum >= target)
                {
                    subLength = j - i + 1;
                    res = res < subLength ? res : subLength;
                    break;
                }
            }
        }
        return res == INT32_MAX ? 0 :res;
    }
};

优化思路

滑动窗口

滑动窗口的核心思路是不断的调整窗口的位置,使得窗口内的元素符合某个条件,找到最优解

滑动窗口的三个主要问题

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?

面对这个题,我们要做的是让窗口内的元素的和大于target,通过不断的移动窗口,找到窗口的最小范围,即结束位置-起始位置的最小值

核心代码(移动窗口)

 while (sum >= s) {
                subLength = (j - i + 1); // 取子序列的长度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置),即窗口向右移动
            }

LeetCode代码

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int res = INT32_MAX;
        int subLength = 0;
        int sum = 0;
        int i = 0;
        for(int j = 0;j < nums.size();j++)
        {
            sum += nums[j];
            while (sum >= target)
            {
                subLength = j - i + 1;
                res = res < subLength ? res : subLength;
                sum -= nums[i++];
            }
        }
        return res == INT32_MAX ? 0 : res;
    }
};

 

 

 
posted @ 2023-03-11 23:16  写在风中的信  阅读(20)  评论(0编辑  收藏  举报