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

61 阅读3分钟

我正在参加「掘金·启航计划」

977.有序数组的平方

代码随想录 (programmercarl.com)

自己看到题目的第一想法

第一想法就是遍历这个数组,平方后再进行排序。正好js有内置的排序方法,十分的方便。

var sortedSquares = function(nums) {
    for(let i = 0;i < nums.length;i++) {
        nums[i] *= nums[i] 
    }
    nums.sort((a,b) => {return a - b})
    return nums
};

看完代码随想录之后的想法

发现还可以使用双指针来解这道题。平方后的数组不是有序的因为负数平方后可能会变得比正数大。这样数组两边的数平方后可能会变最大。

所以可以通过双指针分别指向数组的开头和末尾,通过比较得出比较大的数,再新建一个数组将大数存入,如下所示:

var sortedSquares = function(nums) {
    let i = 0,j = nums.length - 1,k = nums.length - 1
    let arr = new Array(nums.length).fill(0)
    while(i <= j) {
        let left = nums[i] * nums[i]
        let right = nums[j] * nums[j]
        if(left < right) {
            arr[k--] = right
            j--
        } else {
            arr[k--] = left
            i++
        }
    }
    return arr
};

自己实现过程中遇到哪些困难

对于双指针这种解法,通过动图可以理解函数的主要逻辑,但是在函数循环条件那里有些犹豫。什么时候结束,最后一个数如何放入数组中。这是需要仔细考虑的地方。

209.长度最小的子数组

自己看到题目的第一想法

首先的想法就是找出所有的可能比较大小。

这个就不用暴力的方法了,虽然可以解出来,但是时间复杂度很高。

看完代码随想录之后的想法

使用滑动窗口的方式解题,首先利用双指针,右面的指针不断向后,计算和,直到和大于或等于target。

和大于或等于target时需要左面的指针向后,也就是窗口收缩。使用一个变量记录当前最小的窗口长度。重复这个过程。

var minSubArrayLen = function(target, nums) {
    let len = nums.length
    let l = r = sum = 0
    let res = len + 1
    while(r < len) {
        sum += nums[r++] 
        while(sum >=target) {
            res = res < r - l ? res : r - l
            sum -= nums[l++]
        }
    }
    return res > len ? 0 : res
};

自己实现过程中遇到哪些困难

窗口收缩时同时要记录窗口的大小。

59.螺旋矩阵

自己看到题目的第一想法

没想法,哈哈,有点转不过来就去看题解了。

看完代码随想录之后的想法

看过题解视频,自己又画了几种情况后,渐渐有些理解。转的圈数与n有关,为 Math.floor(n / 2),其中如果n为奇数,最中间有一个格子没有转到,需要单独处理。也就是需要中间的格子的位置,是res[mid][mid]

接下来就是每次循环,为了简化循环最好循环不变量,这里我们规定循环为左闭右开。

image.png

图有点丑不要介意,每个小循环为一个颜色,右侧为开区间。这样可以控制循环。

var generateMatrix = function(n) {
    let startX = 0,startY = 0,offset = 1,num = 1
    let count = Math.floor(n / 2) // 旋转圈数
    let mid = Math.floor(n /2) // 奇数的中间位置
    let res = new Array(n).fill(0).map(() => new Array(n).fill(0)) // 二维数组
    while(count--) {
        let i = startX // 行的起始位置 
        let j = startY // 列的起始位置
        for(;j < n - offset;j++) {
            res[startX][j] = num++
        }
        for(;i < n - offset;i++) {
            res[i][j] = num++
        }
        for(;j > startY;j--) {
            res[i][j] = num++
        }
        for(;i > startX;i--) [
            res[i][j] = num++
        ]
        startX++ // 起始位置加一
        startY++
        offset++
    }
    if(n % 2) {
        res[mid][mid] = num
    }
    return res
};

自己实现过程中遇到哪些困难

看过视频后自信满满开始写代码,一执行错了,呜呜,开始debug。

image.png

看出错的地方就知道从右到左和从下到上两个过程没有问题。一顿排查发现自己数组列写错了。res[i][startX] = num++,应该是res[i][j] = num++

数组总结

今天结束了数组的学习,进行数组的总结:

数组是存放在连续内存空间上的相同类型数据的集合,由于数组存放在连续内存空间的特性,删除、增加元素的时候,需要移动其他元素的位置。例如27.移除元素,并不是简单的根据索引删除元素,需要使用后面的元素覆盖被删除的元素。

如果数组是不重复的并且有序(也可以先进行排序),查找元素的时候就应该考虑二分法。进行二分法的时候要坚持对区间的定义,比如一直坚持左闭右开或左闭右闭。考虑数据在定好的区间中是否有意义。

数组的题也常常可以用双指针方法实现,可以有效降低复杂度。

对于滑动窗口这类题,需要理解窗口的移动,确定窗口的起始位置。