第二天 | LeetCode:977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II


题目链接:997.有序数组的平方

1.1 暴力解法

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

1.2重点

  • vector可以用sort(nums.begin(),nums.end())排序
  • 快速排序的sort(nums.begin(),nums.end())时间复杂度为O(n+nlogn)化简为O(nlogn)

思考:怎么变成时间复杂度为O(n)?

2.1双指针解法

代码随想录官方题解

||因为该题目是有序数组,所以最大值一定再两边,不可能在中间 ,可以用双指针 来比较两边再存入一个数组中。把两个for循环压缩成一个for循环

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

2.2理解:比较两边数组找出局部最大值,并存入新数组中,直到旧数组中的元素全部遍历完为止。

2.3重点:      vector<int>result(nums.size(), 0);必须先开辟空间才能使用result[],开辟空间格式为开辟nums,size()大小的空间,并赋初值为0,也可以省去第二个参数。

快慢指针(这里快慢指针是左右边都行)

  • 快指针:右边(假设),找最大值赋给新数组,没找到下边不变

  • 慢指针:左边(假设),找最大值赋给新数组,没找到下标不变


题目链接:209 长度最小的数组

1.1暴力解法

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

1.2结果:超时了,时间复杂度为O(n*n)

2.1滑动窗口(特殊双指针)解法

链接:官方题解

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int size = nums.size();
        int result = INT32_MAX;
        int res = 0;
        for (int i = 0, j = 0; j < size; j++) {
           res += nums[j];
           while (res >= target) {
               int sublength = j - i + 1;
               result = result < sublength ? result : sublength;
               res -= nums[i++];
           } 
        }
        return result == INT32_MAX ? 0 : result;
    }
};
  • 所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

       用一个for循环来做2个for循环所做的事情。----->其实双指针都是这样

2.2思考:这个索引下标 j 表示的究竟是滑动窗口里面的终止位置还是起始位置

可以先假设 j 表示的是起始位置,

j 表示的是起始位置,这个for循环一次一次把索引下标向后移动,这个终止位置要把后面所有的元素遍历一遍,才能返回所有以这个起始 i 为起始位置的集合,然后我们再去判断这个集合里面的所有元素:

如果 >= target,去搜集所有 >= target这些集合里面的所有的长度,再取一个最小的。 如果终止位置是一个一个向后移动的话,那么和这个暴力的解法又存在什么区别呢?

此时你会发现:如果这一个for循环里面的这个j表示的是起始位置的话,那终止位置依然要把所有位置都遍历一遍,那么它的思路就和暴力是一样的。

因此这一个for循环里面的j一定指向的是终止位置,而起始位置需要我们用动态移动的策略来移动起始位置,这样才能用一个for循环的思路来解决这道题。

总结:如果是起始位置就相当于暴力

起始位置和结束位置

窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。直到移动到小于目标值结束位置再开始移动寻找新的集合(移动窗口)

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。当集合内和小于目标值时开始移动

总结:寻找窗口+缩小窗口+移动窗口

为什么时间复杂度是O(n)

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



​​​​​​​​​​​​题目链接:59. 螺旋矩阵 II

 

金牌讲解讲解1    讲解2

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>>result(n, vector<int>(n, 0)); //结果
        int loop = n/2; //可以转几圈
        int startx = 0;
        int starty = 0; //起始坐标
        int offset = 1; //可以控制结束位置
        int count = 1; //数组中的具体值
        while (loop--) {
            int i = startx;
            int j = starty;
            //分别遍历四边并赋初值
            for (j = starty; j < n - offset; j++) { //设置为左闭右开可以一劳永逸,每边都可以这样写
                 result[i][j] = count++;
            } 
            for (i = startx; i < n - offset; i++) {
                 result[i][j] = count++; 
            }
            for (; j > starty; j--) {
                 result[i][j] = count++;
            }
            for (; i > startx; i--) {
                 result[i][j] = count++;
            }
            startx++;
            starty++;

            offset++;
       
        }

         //如果n是奇数就留下了中间的值没有赋值
        if (n % 2 == 1) {
            result[n/2][n/2] = count;
        }
        return result;
        
    }
};

理解:

1.我们可以看出只要一圈一圈的赋值就能赋值成功,所以要用到四个for循环

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

2.当我们遍历一圈时,有很多的边界问题让我们十分烦,必须按照规则来

这里的边界规则为   左闭右开(最后一个不赋值,留给下一个for循环)

3.奇数和偶数圈不一样,如果是奇数的话,中间元素还是没有赋值,所以要特判


         //如果n是奇数就留下了中间的值没有赋值
        if (n % 2 == 1) {
            result[n/2][n/2] = count;
        }
        return result;

4. 二维容器赋值方式

     vector<vector<int>>result(n, vector<int>(n, 0)); //结果

第一个参数为个数,第二个为值是什么。

需要什么变量再去定义什么变量

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值