day2:977.有序数组的平方,209.长度最小的子数组,59.螺旋矩阵II

day2:977.有序数组的平方,209.长度最小的子数组,59.螺旋矩阵II


977.有序数组的平方


题目链接


思路

需要采用时间复杂度为O(n)的算法,则不能采用sort排序算法,需要边排序边平方。但是时间复杂度为O(n)的排序算法不知道有哪些。所以这道题不会。


结果

FAILED!(┬┬﹏┬┬)


反思

没有想到先利用题目原有的升序规则。


代码

 //双指针
 class Solution {
 public:
     vector<int> sortedSquares(vector<int>& nums) {
         int n = nums.size();
         int negative = -1;
         for (int i = 0; i < n; ++i) {
             if (nums[i] < 0) {
                 negative = i;//最小的负数
             } else {
                 break;
             }
         }
 ​
         vector<int> ans;
         int i = negative, j = negative + 1;
         while (i >= 0 || j < n) {//或,没有都超出范围就继续执行。
             if (i < 0) {//负数没有了
                 ans.push_back(nums[j] * nums[j]);
                 ++j;
             }
             else if (j == n) {//正数没有了
                 ans.push_back(nums[i] * nums[i]);
                 --i;
             }
             else if (nums[i] * nums[i] < nums[j] * nums[j]) {
                 ans.push_back(nums[i] * nums[i]);//放小的
                 --i;
             }
             else {
                 ans.push_back(nums[j] * nums[j]);
                 ++j;
             }
         }
 ​
         return ans;
     }
 };
 //解法2
 class Solution {
 public:
     vector<int> sortedSquares(vector<int>& A) {
         int k = A.size() - 1;
         vector<int> result(A.size(), 0);
         for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素
             if (A[i] * A[i] < A[j] * A[j])  {
                 result[k--] = A[j] * A[j];
                 j--;
             }
             else {
                 result[k--] = A[i] * A[i];
                 i++;
             }
         }
         return result;
     }
 };


总结

双指针

双指针的写法,解法2更简单,解法1while更复杂,解法1用的容器push_back,通用性更高吧。day1:27题的也是头尾双指针。


209.长度最小的子数组


题目链接


思路

像一个数学问题。如果是常规思路,就是一个长度一个长度的计算,每个长度的子数组总和都算出来,看到哪个长度的时候超过target,但是这样计算量是不是太大了。

解法1:将从每个元素开始的总和超过target的子数组长度记录下来,最后取最小值。(如果超过n则返回0)

解法2:同理,但是不计算每个最小值,计算从第一个开始的子数组最小长度然后以此为区间向后平移,如果区间总和超过target,则减小区间看能不能满足条件,如果能,则更新区间长度,接着向后平移。(比解法1应该要快一些)


结果

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

反思

  • 有点儿进步,因为while循环里面用if-else,避免重复检测实现了,使代码更精简。

  • min的赋值太麻烦了,直接给个最大值即可让他在循环里自己缩小。

  • sum值忘记在循环尾里面置0了。

  • 这方法不行,每个元素还是操作了很多次,超时了,而且逻辑有错误,j<i+min超范围了,min更新应该用min = min < j - i?min:j-i;


代码

 class Solution {
 public:
     int minSubArrayLen(int s, vector<int>& nums) {
         int result = INT32_MAX;
         int sum = 0; // 滑动窗口数值之和
         int i = 0; // 滑动窗口起始位置
         int subLength = 0; // 滑动窗口的长度
         for (int j = 0; j < nums.size(); j++) {
             sum += nums[j];
             // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
             while (sum >= s) {
                 subLength = (j - i + 1); // 取子序列的长度
                 result = result < subLength ? result : subLength;
                 sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
             }
         }
         // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
         return result == INT32_MAX ? 0 : result;
     }
 };


总结

滑动窗口
  • 重点是那个sum-=nums[i++], 这样可以一直变更起始位置,保持当前最小长度。(先操作再减)而且while里面只有减到小于s才能退出循环。sum减前面的值在更新result后面,不用担心更新错。

  • i++是先操作再加,


59.螺旋矩阵II


题目链接


思路

初始化肯定是n*n,然后根据顺序填入数字,主要是二维数组的索引变化条件。把索引全部写出来应该就能找出规律了。

i,j初始值都为1,然后都按顺序增到n再减,在循环内用while,或者for判定哪个递增或者递减的条件。然后只需要在大循环中更新初始值(1,2,3等)和结束值(n,n-1,n-2等)结束循环条件应该是初始值小于结束值。

更新条件为:1、j完成一次递增,则i的初始值加1

2、i完成一次递增,则j的结束值减1

3、j完成一次递减,则i的结束值减1

4、i完成一次递减,则j的开始值加1


结果

嘿嘿,做出来了!😍

 class Solution {
 public:
     vector<vector<int>> generateMatrix(int n) {
         vector<vector<int>> nums(n,vector<int>(n));
         int i_begin = 0, i_end = n,j_begin = 0, j_end = n;
         int i = i_begin,j = j_begin, index = 1;
         while(i_begin<=i_end&&j_begin<=j_end){
             while(i == i_begin && j < j_end){
                 nums[i][j++] = index++;
             }
                 i_begin++,index--;
             while(j == j_end && i < i_end){
     
                 nums[i++][j-1] = index++;
             }
                 j_end--,index--;
             while(i == i_end && j > j_begin){
                 nums[i-1][--j] = index++;
             }
                 i_end--,index--;
             while(j == j_begin && i > i_begin){
                 nums[--i][j] = index++;
             }
                 j_begin++,index--;
         }
         return nums;
     }
 };


反思

感觉有些代码有点多余,应该可以优化。

边界条件试错了很久。


代码

这方法好像没我的好

 class Solution {
 public:
     vector<vector<int>> generateMatrix(int n) {
         vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
         int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
         int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
         int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
         int count = 1; // 用来给矩阵中每一个空格赋值
         int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
         int i,j;
         while (loop --) {
             i = startx;
             j = starty;
 ​
             // 下面开始的四个for就是模拟转了一圈
             // 模拟填充上行从左到右(左闭右开)
             for (j; j < n - offset; j++) {
                 res[i][j] = count++;
             }
             // 模拟填充右列从上到下(左闭右开)
             for (i; i < n - offset; i++) {
                 res[i][j] = count++;
             }
             // 模拟填充下行从右到左(左闭右开)
             for (; j > starty; j--) {
                 res[i][j] = count++;
             }
             // 模拟填充左列从下到上(左闭右开)
             for (; i > startx; i--) {
                 res[i][j] = count++;
             }
 ​
             // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
             startx++;
             starty++;
 ​
             // offset 控制每一圈里每一条边遍历的长度
             offset += 1;
         }
 ​
         // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值(n/2有余数)
         if (n % 2) {
             res[mid][mid] = count;
         }
         return res;
     }
 };

总结

边界条件

主要还是考虑边界条件别出错和重复了。

  • 18
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值