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

61 阅读4分钟

977.有序数组的平方

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

文章讲解:programmercarl.com/0977.%E6%9C…

视频讲解: www.bilibili.com/video/BV1QB…

思路

本题除了逐一比较的暴力解法,更好的办法是使用双指针。本题数组有负数,所以平方后需要重排。越小的负数平方后越大。数组在原位平方后,从两边向中间靠拢,数值越来越小。那么我们可以想到,构建一个空数组用于存储最终排序结果,并使用双指针从两边开始遍历比较直到中间最小值,每次比较结果大的值就从新数组的尾部依次向前存储。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int[] ans = new int[nums.length];
        int left = 0, right = nums.length - 1;
        int index = ans.length - 1;
        while(left <= right)
        {
            if(nums[left] * nums[left] < nums[right] * nums[right]){
                ans[index--] = nums[right] * nums[right];
                right--;
            }else
            {
                ans[index--] = nums[left] * nums[left];
                left++;
            }
        }
        return ans;
    }
}
  1. 时间复杂度 O(n)

    • 算法的核心是一个 while 循环,它从数组的两端向中间遍历。无论数组的内容如何,每个元素都恰好被访问一次。因此,遍历的次数与数组 nums 的长度 n 直接相关。 在循环中的每一步,算法执行一些常数时间的操作。
  2. 空间复杂度 O(n)

    • 空间消耗主要由新创建的数组 ans 决定,其大小与输入数组成线性关系,因此空间复杂度为 O(n),其中 n 是数组 nums 的长度。

209.长度最小的子数组

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

文章讲解:programmercarl.com/0977.%E6%9C…

视频讲解: www.bilibili.com/video/BV1QB…

思路

这道题可以使用滑动窗口进行解决,其主要运动方式是,快指针进行数组遍历(扩展窗口),慢指针在后面进行窗口收缩。

对于本题,开始时,窗口大小为0。我们逐渐扩大窗口(增加窗口的右边界),直到窗口内的元素总和达到或超过 target。一旦实现这个条件,我们就尝试通过缩小窗口(即增加窗口的左边界)来找到更小的满足条件的子数组,同时继续维护元素总和。在整个数组中重复这个过程,我们能找到满足条件的最短子数组。如果找不到这样的子数组,我们返回 0。这个方法的效率在于它避免了重复检查每个可能的子数组,而是通过一次遍历来找到答案。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int sum = 0;
        int len = Integer.MAX_VALUE;
        int i = 0, j = 0;
        while(j < nums.length)
        {
            sum += nums[j++];
            while (sum >= target) {
                len = Math.min(len, j - i);
                sum -= nums[i++];
            }
        }
        return len == Integer.MAX_VALUE ? 0 : len;
    }
}

时间复杂度是 O(n),空间复杂度是 O(1),因为只需要一次遍历数组,并且只使用了固定数量的额外空间。

59.螺旋矩阵II

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

文章讲解:programmercarl.com/0059.%E8%9E…

视频讲解:www.bilibili.com/video/BV1SL…

思路

这道题没有什么所谓的简单快捷方法,主要考察的是“模拟矩阵”,难点在于边界的确认。

public class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];
​
        int num = 1; // 初始化起始数字
        int rowStart = 0;
        int rowEnd = n - 1;
        int colStart = 0;
        int colEnd = n - 1;
​
        while (rowStart <= rowEnd && colStart <= colEnd) {
            // 遍历顶行
            for (int j = colStart; j <= colEnd; j++) {
                matrix[rowStart][j] = num++;
            }
            rowStart++;
​
            // 遍历右列
            for (int j = rowStart; j <= rowEnd; j++) {
                matrix[j][colEnd] = num++;
            }
            colEnd--;
​
            // 遍历底行
            if (rowStart <= rowEnd) {
                for (int j = colEnd; j >= colStart; j--) {
                    matrix[rowEnd][j] = num++;
                }
            }
            rowEnd--;
​
            // 遍历左列
            if (colStart <= colEnd) {
                for (int j = rowEnd; j >= rowStart; j--) {
                    matrix[j][colStart] = num++;
                }
            }
            colStart++;
        }
​
        return matrix;
    }
}
​

时间复杂度、空间复杂度都是 O(n^2),矩阵维度。

补充解释代码:

  1. 边界定义

    1. Row Start (rowStart): 最上面一行的索引,初始为 0。
    2. Row End (rowEnd): 最下面一行的索引,初始为 n-1
    3. Column Start (colStart): 最左边一列的索引,初始为 0。
    4. Column End (colEnd): 最右边一列的索引,初始为 n-1
  1. 边界更新

在填充过程中,这些边界会根据以下规则更新:

  • 填充上行 (rowStart):colStartcolEnd。填充完成后,rowStart 加1,因为上面的行已经填充完毕。
  • 填充右列 (colEnd):rowStartrowEnd。填充完成后,colEnd 减1,因为右边的列已经填充完毕。
  • 填充下行 (rowEnd): 如果 rowStart <= rowEnd,则从 colEndcolStart。填充完成后,rowEnd 减1,因为下面的行已经填充完毕。
  • 填充左列 (colStart): 如果 colStart <= colEnd,则从 rowEndrowStart。填充完成后,colStart 加1,因为左边的列已经填充完毕。
  1. 边界检查

在每次填充后,需要检查边界条件以确定是否所有行和列都已经被正确处理。这是通过比较 rowStartrowEnd,以及 colStartcolEnd 来实现的。如果 rowStart 超过了 rowEndcolStart 超过了 colEnd,那么意味着所有的数字都已经被填充完毕,这时应该停止进一步的填充。

  1. 结束条件

循环继续进行,直到 rowStart 大于 rowEnd 或者 colStart 大于 colEnd,这意味着整个矩阵已被填满。

数组总结

后面再补充总结链接...