【代码随想录】算法训练营day2 | 力扣977有序数组的平方+209长度最小的子数组+59螺旋矩阵贰

LeetCode977有序数组的平方

代码随想录: 有序数组的平方
力扣: 977

初印象

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

  2. 问题拆解,先平方后排序。注意负数的平方可能和正数平方相等。初步判断暴力解法时间复杂度O(nlogn)?

  3. 暴力排序解法:借助C++ sort函数实现暴力排序,需要加入头文件#include,是一种类快排的排序算法,时间复杂度O(logn)。用法详见链接。

  4. 原来原数组是有序的,由于负数平方后可能超过部分正数,所以数组平方后的最大值(相对最大)只可能出现在两端。所以可以用双指针法

  5. 双指针法建立一个新数组,一个指针k指向新数组末端,从尾部向头部逐个移动填写挨个填写最大值。两个指针i、j指向原数组两端,分别向中间移动进行比较,较大值填向k的位置。

看视频讲解后的想法

  1. for循环i<=j,否则丢项。i++和j–不写进循环体,在语法上是可以的。为什么不写进去?因为我们的i++和j–是有条件的。(用while循环也可以)。

  2. nums[i], nums[j]相等的情况下移动谁都是一样的,所以if后面可以直接用else,即两种情况就能覆盖。

实现过程中遇到的问题

一、暴力解法

//有序数组的平方——暴力解法
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i=0;i<nums.size();i++){
            nums[i]=nums[i]*nums[i];
        }
        sort(nums.begin(),nums.end());
        return(nums);
    }
};
  1. line8 sort函数要填排序的起始和结束位置,用数组的begin和end
  2. line9 返回一个数组

二、双指针法

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int k=nums.size()-1;
        vector<int>result(nums.size(),0);//定义一个size大小相等,初始值为0的数组
        for(int i=0,j=nums.size()-1;i<=j;){
            if(nums[i]*nums[i]<=nums[j]*nums[j]){
                result[k--]=nums[j]*nums[j];
                j--;
            }else{
                result[k--]=nums[i]*nums[i];
                i++;
            }
        }
        return(result);//函数返回一个数组
    }
};

vector用法总结:vector用法详细总结
vector与数组的区别: vector与数组

总结

注意到虽然负数平方后可能超过正数,但在原数组单调不减的情况下,平方后的数组中较大值的出现仅能出现在数组两端。所以可以使用双指针法。时间复杂度和空间复杂度都是O(n)。

LeetCode209长度最小的数组

代码随想录: 长度最小的数组
力扣: 209

初印象

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

  1. 没说有序,需要一个sum和target比较。

  2. 只需返回长度。需要长度最小的。

  3. 当且仅当每一位都大于target才不存在符合条件的子数组?

  4. 大体思路,双指针一个i记录起始位置,一个j记录结束位置,一个sum一个length。i如果大于target直接后移,j从i开始移动每移动一位就sum加一位的数值,直到sum>target(之后i开始后移)或者j已到数组末位。当i移动到末尾而非j移动到数组末尾时循环结束。

  5. 沃柑是sum>target。上面的思路和暴力法无异。因为先确定的是头部再去移动尾部,还是会陷入两个for循环的怪圈。

  6. 用j先向后移动然后用i去追。可以用一个for循环解决问题。

  7. 滑动窗口法:

  • 窗口是什么?窗口是满足sum>target的连续子数组

  • 窗口前端什么时候移动?当sum>target时,向末端移动缩短窗口长度,寻找最小子数组

  • 窗口末端什么时候移动?当sum<=target时末端向后移动,然后换窗口前端移动

  • 窗口前端是从数组最前端开始移动吗?是的

看讲解后的想法

  1. 沃柑是sum>target。上面的思路和暴力法无异。
  2. while循环

实现过程中的问题

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int i=0;
        int sum=0; 
        int length=0;
        int result = INT32_MAX;//result初始值不能设置为0,否则永远不行     
        for(int j=0;j<nums.size();j++){
            sum+=nums[j];
            /*length++;*/
            while(sum>=target){//大于等于而非大于
               
               length=(j-i+1);
               result=length<result?length:result; 
               sum-=nums[i++];//注意这几条的顺序
            }
        }
        return result==INT32_MAX?0:result;//没找到合适的子序列,result还为初始值,返回0
    }
};
  1. 局部变量:循环体内的变量
  2. line7result不能习惯性设置为0,否则怎么和result比较呢?
  3. line10没有用这种length计算方式
  4. line11要用大于等于,虽然是正整数数组,好像等于的情况下i不用再移动了,但是
    如2 3 1 2 4 3中一样,1+2+4的后面可能藏着4+3这种相等的情况。
  5. line15 i++出现,这条放最后面
  6. line18 考虑到没有找到的情况要这样写

总结

这道题双指针法还是十分巧妙,应该跟着代码手写一下算法过程,体会指针移动的情况。

LeetCode59螺旋矩阵

代码随想录: 螺旋矩阵
力扣: 59

初印象

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

  1. 我测好难的题,输入3的话就是3 * 3的矩阵,4是4 * 4的矩阵。还要转弯,要按大小顺序输入吗?最后一个数n2 出现的位置貌似无法确定。

  2. 要按大小顺序输入,根据循环不变量原则,每次循环要做:

  • 从左上到右上
  • 从右上到右下
  • 从右下到左下
  • 从左下到左上
  1. 画边怎么描述位置?每画一条边都要遵循左闭右开(或者左开右闭原则)?

看讲解后的想法

  1. 为什么圈数是n/2?
  • 因为搜索一圈之后,下一圈的上边会往下走,下一圈的下边会往上走,高度就少2,下一圈的左边会往右走,下一圈的右边会往左走,相当于宽少2了,每次下一圈都会比上一圈的高度宽度都少2,直到这个没有外圈了,没有外圈就是宽度是和高度都是0的时候(偶数的情况),每次宽,高缩小2,直到宽,高是0。因为宽,高都一样,而且是一起缩2的,那么就当高缩2到0的时候就结束了,要缩多少次,就是多少圈。高假设是偶数,偶数-2-2-2一直到0,不就是这个偶数除2吗,就是圈数
  1. 二维矩阵,startx和starty记录边开始的位置
  2. int loop = n / 2; loop每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
  3. int mid= n/2; 记录矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
  4. int offset=1;每次边长减一

实现过程中的问题

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 = starty; j < n - offset; j++) {
                res[startx][j] = count++;
            }
            // 模拟填充右列从上到下(左闭右开)
            for (i = startx; 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为奇数的话,需要单独给矩阵最中间的位置赋值
        if (n % 2) {
            res[mid][mid] = count;
        }
        return res;
    }
};

看不懂,我玩植物人的。

总结

边界条件众多,还是遵循循环不变量原则,太难了没写出来

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值