【LeetCode 刷题笔记】10.有序数组的平方 11.长度最小的子数组

10.有序数组的平方(977)

题目

给你一个按 非递减顺序 排序的整数数组 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]

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非递减顺序 排序

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题

思路及题解

  • 解法一:暴力解法,不再赘述

    class Solution {
    public:
        vector<int> sortedSquares(vector<int>& nums) {
            for(int i=0;i<nums.size();i++){
                nums[i] *= nums[i];
            }
            sort(nums.begin(),nums.end());
            return nums;
        }
    };
    
  • 解法二:双指针法

    • 数组 nums 已经按照升序排列,那么,我们可以找到负数和非负数的分界线,因为非负数的平方按升序排列,负数的平方按降序排列
    • 找到分界线后,只需要两个指针,一个往非负数方向走,另一个往负数方向走,将它们的平方比较大小,较小的放入新建的Vector中,同时指针移动,这样最后新建的Vector中的数据就会按升序排列
    • 注意指针到达边界的判断条件
    class Solution {
    public:
        vector<int> sortedSquares(vector<int>& nums) {
            vector<int> newNums;
            //找出分界点
            //不能写  while(nums[i]<0)    i++;  特定输入i会越界,如 nums = [-1]
            int negative = -1;
            for (int i = 0; i < nums.size(); ++i) {
                if (nums[i] < 0) {
                    negative = i;
                } else {
                    break;
                }
            }
    
            int i = negative;
            int j = i + 1;
            while(i>=0 || j<nums.size()){
                if(i<0){//左边指针已经走完
                    newNums.push_back(nums[j]*nums[j]);
                    j++;
                }
                else if(j == nums.size()){//右边指针已经走完
                    newNums.push_back(nums[i]*nums[i]);
                    i--;
                }
                else if(nums[i]*nums[i] < nums[j]*nums[j]){//放入较小的数
                    newNums.push_back(nums[i]*nums[i]);
                    i--;
                }
                else{//数字大小相同时,随便放一个数
                    newNums.push_back(nums[j]*nums[j]);
                    j++;               
                }
            }  
            return newNums;
        }
    };
    
  • 解法三:优化的双指针法

    • 可设置对撞指针,从两边的数字开始添加。由于两边的数字的平方是较大的数,因此添加到新的数组中时要逆序添加,且较大的数需要先加入。这样做的好处是不用判断边界条件,当左右指针交错时,所有的数已经放入,循环停止
    class Solution {
    public:
        vector<int> sortedSquares(vector<int>& nums) {
            vector<int> newNums(nums.size());
            for(int i=0,j=nums.size() - 1, pos = nums.size() - 1;i<=j;pos--){
                if(nums[i]*nums[i] > nums[j]*nums[j]){
                    newNums[pos] = nums[i]*nums[i];
                    i++;
                }
                else{
                    newNums[pos] = nums[j]*nums[j];
                    j--;
                }
            }
            return newNums;
        }
    };
    

11.长度最小的子数组(209)

题目

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 连续

子数组

[numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0

示例 1:

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

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

进阶:

  • 如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

思路及题解

  • 解法一:暴力解法

    • 两层 for 循环,不断查找符合条件的子序列(该解法在力扣会超时)
    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;
        }
    };
    
  • 解法二:滑动窗口

    • 涉及求连续的子数组问题时,可以考虑滑动窗口

    • 滑动窗口,其实也是一种双指针。不断调整两个指针之间的距离,得出我们想要的结果

    • 首先要思考 如果用一个for循环,那么参数 i 应该表示 滑动窗口的起始位置,还是终止位置。

      如果 i 表示的是起始位置,那么怎么遍历剩下的终止位置呢?另一个指针只能从这个指针的位置开始遍历,那么这又变成了暴力解法

    • 所以,一层for循环的参数应该表示滑动窗口的终止位置

    • 首先慢指针和快指针都指向初始位置。循环开始,快指针向前不断移动,知道经过的数字的和 sum 大于等于 target ,此时移动慢指针,并且不断更新最小数组长度 result 以及经过的数字的和 sum。若某次移动慢指针后的和小于 sum,则继续移动快指针,直到 sum 再次大于 target,如此循环往复

    • result 的初始值设为一个很大的值(最好是解法一中Int的最大值),如果最后这个值没改变,说明没有符合条件的子序列,返回0

    • 这种方法大大减少了比较子数组的次数,滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

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

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

你可能感兴趣的:(LeetCode刷题笔记,leetcode,笔记,算法)