# 代码随想录算法训练营第二天|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

67 阅读3分钟

977.有序数组的平方

题目链接:leetcode.cn/problems/sq…

第一想法

先把每个数平方了,再对新数组排序

JS代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortedSquares = function(nums) {
    let num = [];
    let i = 0,j = 0, n = 0;
    for(i = 0; i < nums.length; i++){
        num[i] = nums[i] * nums[i]
    }
    for(i = 0; i < num.length; i++){
        for(j = 0; j < num.length - i - 1; j++){
            if(num[j] > num[j+1]){
                n = num[j+1];
                num[j+1] = num[j];
                num[j] = n;
        }
            
            }
    }
    return num;
};

这里使用了冒泡法排序,时间复杂度为O(N+NlogN)

思路

其实数组本身就是有序的,所以最大值一定在两端,不可能是中间

即,问题可以被转化为,最大的值是在左边还是右边,第二大的值是在左边还是右边,以此类推

因此,可以使用左右两个指针对数组进行操作

JS 代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortedSquares = function(nums) {
    
    let left = 0, n = nums.length - 1;
    let right = n, i = n;
    let num = new Array(n).fill(0);
​
    while(left <= right){
        if(nums[left]*nums[left] >= nums[right]*nums[right]){
            num[i--] = nums[left]*nums[left];
            left++;
        }else if(nums[left]*nums[left] < nums[right]*nums[right]){
            num[i--] = nums[right]*nums[right];
            right--;
        }
    }
    return num;
};

总结

js的数组的初始化没写出来,尴尬

这道题注意是依次把最大值找出来,所以,需要先建立一个全是0的数组

再对数组进行赋值

209.长度最小的子数组

题目链接:leetcode.cn/problems/mi…

第一想法

两个循环,不断寻找符合条件的子序列

显然,暴力解法的复杂度实在太高了

思路

滑动窗口思想

不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。

for循环中的参数是滑动窗口的终止位置

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(子序列的起始位置)
            }

滑动窗口的精妙之处在于此,先通过一点点增加终止位置的值,找到一个满足条件的范围,这个范围作为窗口

然后对窗口进行操作,如果元素和大于target则起始位置左移

如果元素和小于target则终止位置右移

判断每次满足条件的窗口的大小是不是当前最小值,如果是的话,可以更新

JS代码如下:

/**
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function(target, nums) {
    let result = Infinity, i = 0, j = 0, sum = 0, len = 0;
    for(j = 0; j < nums.length; j++){
        sum += nums[j];
        while(sum >= target){
            len = j - i + 1;
            result = len < result? len : result;
            sum -= nums[i++]
        }
​
    }
    return result == Infinity? 0 : result;
};

总结

这一题的思路非常重要,精妙之处在于遍历终止位置,调节起始位置来得出窗口的大小

另外,这一题需要细心

在写的过程中,sum -= nums[i++]这一步漏掉了

窗口缩小时不要忘记sum要减去对应的值!

59.螺旋矩阵II

题目链接:leetcode.cn/problems/sp…

第一想法

完全不会,不知道怎么下手

思路

这里全部统一按照左闭右开来写(参照卡哥的图)

为了方便,设置好每次循环一圈的初始位置:startx 和 starty,每循环完一圈初始位置都要+1

同时,每一圈的终点位置相比于上一圈也会减一,因此设置变量offset

最后,需要考虑的是n是奇数还是偶数,循环的圈数loop = n/2

如果n是奇数,则循环结束后最中间的点还没有被赋值,需要另外赋值

JS代码如下:

/**
 * @param {number} n
 * @return {number[][]}
 */
var generateMatrix = function(n) {
    let res = new Array(n).fill(0).map(()=>new Array(n).fill(0));
    let i = 0, j = 0, loop = 0, num = 1;
    let startx = 0, starty = 0, offset = 1,mid = parseInt(n/2);
    loop = parseInt(n/2);
    while(loop--){
        // 设置好循环的初始位置和终止位置
        for(j = starty; j < n - offset; j++){
            res[startx][j] = num++;
    }
        for(i = startx; i < n - offset; i++){
            res[i][j] = num++;
        }
        for(;j > starty; j--){
            res[i][j] = num++;
        }
        for(;i > startx; i--){
            res[i][j] = num++;
        }
        // 循环完一圈,下一圈初始位置+1,终止位置-1
        starty++;
        startx++;
        offset++;
    }
    // 如果是奇数,最后给中间点赋值
    if(n % 2){
        res[mid][mid] = num;
    }
    return res;
};

总结

二维数组这道题写了好久,一开始没有用初始位置和终止位置以及offset这几个变量,导致整体思路比较乱

理清楚每次循环的初始位置和终止位置,就会清楚的多

另外,对于最后给中间点赋值这一步,一开始是直接res[i][j] = num;

因为循环结束后,i和j的位置刚好是中间点

但是这里忽略了一个问题,作用域外面是无法访问作用域内部的变量的,因此,这里的`res[i][j]实际上是res[0][0]

导致结果出现错误