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

文章介绍了在LeetCode上解题时遇到的问题及解决方案,涉及有序数组的平方问题,使用双指针优化从O(n^2)到O(n)的时间复杂度,以及滑动窗口方法在求解最小子数组长度问题中的应用,降低了时间复杂度。同时,讨论了螺旋矩阵问题的遍历策略和边界处理。
摘要由CSDN通过智能技术生成

997.有序数组的平方

题目链接 977. 有序数组的平方 - 力扣(Leetcode)
遇到问题

​ 再运用双指针时,没联想到新建一个答案数组,导致无法解答

题解
1. 暴力遍历解法

最简单的思想就是全部算出来,然后排序

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

时间复杂度: O(n + nlogn)(取决于快速排序的复杂度)
空间复杂度: O(1)

2. 双指针解法

分析可知,数组平方后出现的情况是负数平方大于正数,而且是负的越多越大,对于数组中负数来说,左边的平方一定大于右边的平方;对于正数来说,右边的平方一定大于左边的平方,因此,最大的数值只能出现在两头,故只需要比较两头大小即可

var sorteSquares = function(nums){
    let res = [], i = 0, j = nums.length - 1
    while(i <= j){
        let left = nums[i] * nums[i]
        let right = nums[j] * nums[j]
        if(left > right){
            res.unshift(left)  // 将数压入最前端,保证为非递减
            i ++
        }else{
            res.unshift(right)
            j --
        }
    }
    return res
}

时间复杂度:O(n)
空间复杂度:O(n)(为保存答案数组所占用的空间)

当然如果不想使用Arry.unshift()可以创建一个指针k在每次赋值时用nums[k--]即可
需要额外注意的是:循环条件中i <= j应该是需要赋值等号,如果未赋值,则当遍历到两个指针重合时,会导致少压入一个元素

209.长度最小的子数组

题目链接 209. 长度最小的子数组 - 力扣(Leetcode)
遇到问题

没完全理解滑动窗口和涵盖关系,错误的把暴力解法认为是滑动窗口法,从而没做出更好的解答

题解
1.暴力解法
var minSubArrayLen = function(target, nums) {
   	let minLength = Infinity
    for(let i = 0; i < nums.length; i++){
        for(let j = i,sum = 0; j < nums.length; j++){
            sum += nums[j]
            if(sum >= target){
                minLength = minLength > (j - i + 1) ? (j - i + 1) : minLength
                break
            }
        }
    }
    return minLength == Infinity? 0: minLength
}

时间复杂度:O(n^2)
空间复杂度:O(1)

基本思路就是一个数一个数的算然后找出来最小的值,没什么可以过多说的

2.滑动窗口法

滑动窗口 的基本思想是:就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果
其实这也是双指针的一种,但是他多了一个固定的区间,具体到这个题目中,需要确定

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?

这里借用一下代码随想录的动态图来更好的理解这过程
在这里插入图片描述

var minSubArrayLen = function(target, nums) {
    let minLength = Infinity, sum = 0
    let i = 0  //窗口起始位置
    for(let j = 0; j < nums.length; j++){
        sum += nums[j]
        while(sum >= target){
            minLength = minLength > (j - i + 1)? (j - i + 1) : minLength
            sum -= nums[i++]   // 起始位置向右边移动一位
        }
    }
    return minLength == Infinity? 0 : minLength
}

时间复杂度:O(n)
空间复杂度:O(1)

乍一看会和暴力解法很像,但是精髓之处,就是动态的计算了框选了一个长度,即循环中的while判断而不是无止尽的遍历

可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)

扩展

904. 水果成篮 - 力扣(Leetcode)
76. 最小覆盖子串 - 力扣(Leetcode)

59.螺旋矩阵ll

题目链接 59. 螺旋矩阵 II - 力扣(Leetcode)
遇到问题
  1. 只想着如何快速解答,直接上手找规律,没有进行实际模拟
  2. 对整个区间规划,做的不完善
题解

题目重点是如何转圈遍历,然后模拟过程,其次是与二分法相似的循环不变量原则
在本题中,循环不变量原则体现在对每一个转角的模拟遍历,是属于转角前的还是转角后的呢?

先看代码

var generateMatrix = function(n) {
    let res = new Array(n).fill(0).map(() => new Array(n).fill(0))
    let loop = n >> 1  // 判断转圈数
    let mid = n >> 1  // 中间值
    let count = 1 // 计数
    let offset = 1 // 每一行减少数
    let startX = startY = 0
    while(loop--){
        let row = startX,col = startY
        // 上行从左往右遍历(左闭右开)
        for(; col < n - offset; col++){
            res[row][col] = count++
        }
        // 右边从上往下遍历(左闭右开)
        for(; row < n - offset; row++){
            res[row][col] = count++
        }
        // 下行从右往左遍历(左闭右开)
        for(; col > startY; col--){
            res[row][col] = count++
        }
        //左边从下往上(左闭右开)
        for(;row > startX; row --){
            res[row][col] = count++
        }
        startX++
        startY++
        offset += 1
    }
    // 判断是否为基数
    if(n % 2 != 0){
        res[mid][mid] = count
    }
    return res
 }

时间复杂度: O(n^n)
空间复杂度:O(n^n)

这道题目没有很多技巧性质的方法,主要就是在模拟这个转圈赋值的过程,但是这个过程细节很多:

    • Q: 如何遍历每个矩阵中的值
    • A: 依照转圈的方式,分别遍历上右下左
    • Q: 怎么样算一个循环
    • A:按照上述的遍历,循环的是每圈,即每完成一次上右下左遍历后为一个周期,故因此需要提前知道有多少圈
    • Q: 边界值如何分配

    • A:应该按照循环不变量原则始终按照一种区间的开闭,这里最好采用左闭右开,借用图,如图:


      ​此外做这种类型的题目时,应该不急躁一步一步模拟自己的人工运算规则

扩展

54. 螺旋矩阵 - 力扣(Leetcode)
剑指 Offer 29. 顺时针打印矩阵 - 力扣(Leetcode)

今日总结

  1. 进一步理解的深入了双指针的用法
  2. 学习滑动窗口如何使用
  3. 今日将代码随想录的数组部分学习完毕,其中主要学习到的方法
    想录的数组部分学习完毕,其中主要学习到的方法
    1. 二分法查找:需要主要区间的开闭选择,一般为左闭右开和左闭右闭
    2. 双指针法:将二维遍历降低到一维遍历的常用方法,在数组题目能暴力解答时,算法复杂度为O(n^2)时候不妨往此方向思考
    3. 在双指针法进一步细化的中,学习了滑动窗口的方法,动态的思考一个问题的规划与设计
    4. 在解题过程中不要上来就寻找最优解,应该需要首先自己模拟这个过程尝试用暴力法解决,然后再思考有无类似的思想可以运用
    5. 解题的细节注意,尤其是当取=的时候需要格外注意
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值