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


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

知识提要:双指针、

977.有序数组的平方

题目建议: 本题关键在于理解双指针思想
文章讲解:977.有序数组的平方
视频讲解: 双指针法经典题目 | LeetCode:977.有序数组的平方

题目链接leetcode977 squares-of-a-sorted-array
题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

暴力法

思路:拿到这个题,第一反应:先平方,再快排。但是想了下,快排的时复是 O ( n log ⁡ n ) O(n \log n) O(nlogn),不符合题目的条件。故排除。有一说一,脑子里每次蹦出来的方法只有这种方式,感觉方法匮乏,需要再去补补基础。
然后看了下老师建议的思路,用双指针。

双指针

思路:
1.题目中是给了是非递减顺序,但是数组中有负数,负数平方以后,其最大值一定在最左边,那么最大值就只可能出现在两边了。2.用两个指针,一个指针从前面扫描,一个从后面扫描,并且新建一个空数组,用来存放结果,新建一个指针指向数组的最后一位。3.比较两数平方,将较大的数放进新的数组。

代码:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortedSquares = function (nums) {
    // 使用双指针
    let size = nums.length - 1
    let left = 0
    let right = size, k = size
    var result = []
    while (left <= right && k >= 0) {
        if (nums[left] * nums[left] <= nums[right] * nums[right]) {
            result[k--] = nums[right] * nums[right]
            right--
        } else {
            result[k--] = nums[left] * nums[left]
            left++
        }
    }
    return result
}

执行用时: 108 ms
内存消耗: 47.4 MB
时间复杂度为O(n)

总结:刷这个题,遇到了坑有:result用let result = []直接定义,输出的还是[],因为let 声明的范围是块作用域,函数内部结果无法在作用域外进行访问,这里我将result用var定义为了全局变量。

209.长度最小的子数组

题目建议: 本题关键在于理解滑动窗口,这个滑动窗口看文字讲解 还挺难理解的,建议大家先看视频讲解。 拓展题目可以先不做。
文章讲解:209.长度最小的子数组
视频讲解:拿下滑动窗口! | LeetCode 209 长度最小的子数组

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

暴力法

思路:使用双指针,快的指针用来对连续子数组进行累加,慢的数组用来进行下一轮遍历。写完后发现本质上还是暴力法,用这个方法时间很长。
代码思路
代码:

/**
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function (target, nums) {
    //快慢双指针
    let slow = 0;
    let count = 0;
    let min = Infinity
    for (let fast = 0; fast < nums.length; fast++) {
        count += nums[fast]
        if (count >= target) {
            min = Math.min(min, fast - slow + 1);
            count = 0
            fast = slow
            slow++
        }
    }
    if (min == Infinity) {
        return 0
    } else {
        return min
    }
};

执行用时: 1476 ms
内存消耗: 45.2 MB
时间复杂度: O ( n 2 ) O(n^2) O(n2)

总结 :一开始写的时候,先slow++,再fast=slow,但是提交的时候,发现出错了,在进行单步调试的时候发现,fast在for循环的时候执行了fast++,这样写就往后走了一位,在测试用例[1,4,4] target = 4的时候就不通过。本质上还是两个for循环,时间复杂度 O ( n 2 ) O(n^2) O(n2)

滑动窗口法

思路:滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。也是利用两个双指针,只不过,没有对fast指针进行重新赋值循环,而是动态调整滑动窗口的起始位置,来进行判断,这样子少了很多fast循环的操作。
在这里插入图片描述
代码:

var minSubArrayLen = function (target, nums) {
    //滑动窗口法
    let size = nums.length
    let start = 0;
    let count = 0;
    let min = Infinity
    for (let end = 0; end < size; end++) {
        count += nums[end]
        while (count >= target) {
            min = Math.min(min, end - start + 1);
            count -= nums[start]
            start++
        }
    }
    return min === Infinity ? 0 : min
};

执行用时: 76 ms
内存消耗: 45.2 MB
时间复杂度:O(n)
空间复杂度:O(1)
总结 :使用滑动窗口的办法,时间复杂度降低了好多,自己在写的时候,确实没想到可以改变窗口的起始位置,只想到了,要以每一个元素为起点进行相加。滑动窗口的精髓:count -= nums[start]

59.螺旋矩阵II

题目建议: 本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。
文章讲解:59.螺旋矩阵II
视频讲解:一入循环深似海 | LeetCode:59.螺旋矩阵II

题目链接leetcode59 spiral-matrix-ii
题目描述:给你一个正整数 n n n ,生成一个包含 1 到 n 2 n^2 n2 所有元素,且元素按顺时针顺序螺旋排列的 n ∗ n n * n nn 正方形矩阵 matrix 。

思路:
模拟顺时针画矩阵的过程:
填充上行从左到右
填充右列从上到下
填充下行从右到左
填充左列从下到上
最关键的是:循环的边界条件,谨记循环不变量原则。
在这里插入图片描述
代码:

/**
 * @param {number} n
 * @return {number[][]}
 */
var generateMatrix = function (n) {
    let startX = 0
    let startY = 0;   // 起始位置
    let loop = Math.floor(n / 2);   // 旋转圈数
    let mid = n >> 1// 中间位置
    let offset = 1;    // 控制每一层填充元素个数
    let count = 1;     // 更新填充数字
    let res = new Array(n).fill(0).map(() => new Array(n).fill(0));

    while (loop--) {
        let i = startX
        let j = startY
        // 上行从左到右(左闭右开)
        for (; j < n - offset; j++) {
            res[startX][j] = count++;
        }
        // 右列从上到下(左闭右开)
        for (; i < n - offset; i++) {
            res[i][j] = count++;
        }
        // 下行从右到左(左闭右开)
        for (; j > startY; j--) {
            res[i][j] = count++;
        }
        // 左列做下到上(左闭右开)
        for (; i > startX; i--) {
            res[i][j] = count++;
        }

        // 更新起始位置
        startX++;
        startY++;

        // 更新offset
        offset++;
    }
    // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
    if (n % 2 === 1) {
        res[mid][mid] = count;
    }
    return res;
};

执行用时: 60 ms
内存消耗: 41.1 MB
时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
空间复杂度 O(1)

总结 :老实讲,看到题目和提示的思路,还是很懵逼,没有思路,就先看了视频讲解。然后才明白,但是那个边界条件是真的好容易混,明天画个步骤图出来贴上!

总结:今天做题有点慢,感觉思路还是有点不清晰,第三个题拿到的时候,一点儿思路都没有!但是好在学会了!感觉今天充实不少。然后就是基础有点不牢,需要去复习一下数据结构的有关算法了。但是今天拿到题,自己都思考了,就是自己思考的方法不太好。不完善,面对数组,总是第一想到暴力法。还是得多做题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值