leetcode977. 有序数组的平方
注意点:
- 题意是给一个有序数组,因此元素都是从小到大排列的,所以可以用相向双指针法来实现;
- 因为返回的是一个数组,所以要想到构建一个新数组用来存放排序好的元素;
- while() 循环的判断条件里要注意有个=号;
class Solution {
// 相向双指针法
public int[] sortedSquares(int[] nums) {
int len = nums.length;
int left = 0;
int right = len - 1;
// 构建一个相等长度的新数组用来存放排序好的元素;从大排到小,因此指针指向最后一个元素
int[] res = new int[len];
int index = len - 1;
// 注意有个=号不能漏,因为最后要处理两个元素
while (right >= left) {
if (nums[right] * nums[right] >= nums[left] * nums[left]) {
res[index] = nums[right] * nums[right];
index--;
right--;
} else {
res[index] = nums[left] * nums[left];
index--;
left++;
}
}
return res;
}
}
拓展题目:
leetcode209. 长度最小的子数组
这题也是二刷的时候想对了思路,但是实现起来不太顺利。主要在于没弄清楚滑动窗口的起始位置和终止位置的更新方式,还得多写代码才行。
注意点:
- 滑动窗口:不断调节子序列的起始位置和终止位置,从而得出我们要想的结果;
- 在这题中实现滑动窗口法,主要确定这三点:
窗口内是什么——满足其和 ≥ target 的长度最小的连续子数组;
如何移动窗口的起始位置——如果当前窗口的总值大于 target,窗口就要向前移动了(也就是该缩小了,left–);
如何移动窗口的结束位置——窗口的结束位置就是遍历数组的指针,也就是for循环里的索引(right < len)。 - 解题的关键在于窗口的起始位置如何移动;
- 滑动窗口法的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
class Solution {
// 滑动窗口法
public int minSubArrayLen(int target, int[] nums) {
int len = nums.length;
int left = 0; // 滑动窗口起始位置
int sum = 0; // 滑动窗口数值之和
int res = Integer.MAX_VALUE; // 先将结果值定义为整数型最大值
for (int right = 0; right < len; right++) {
sum += nums[right];
// 注意这里使用while,每次更新 left(起始位置),并不断比较子序列是否符合条件
while (sum >= target) {
res = Math.min(res, right - left + 1);
sum -= nums[left];
left++; // 这里体现出滑动窗口的精髓之处,不断变更left(子序列的起始位置)
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return res == Integer.MAX_VALUE ? 0 : res;
}
}
拓展题目:
904.水果成篮(opens new window)
76.最小覆盖子串
leetcode59. 螺旋矩阵II
此题代码主要依据力扣高赞题解,感觉比随想录的答案更好写。
注意点:
- 模拟法;更新
- 循环不变量原则;上下左右4个边界
- 使用 num <= tar 而不是 l < r || t < b 作为循环条件,是为了解决当 n 为奇数时,矩阵中心数字无法在循环过程中被填充的问题。
class Solution {
public int[][] generateMatrix(int n) {
// 定义当前左右上下边界 l,r,t,b
int l = 0;
int r = n - 1;
int t = 0;
int b = n - 1;
// 定义输出矩阵mat,保存从 1 遍历到 n*n 的每个元素
int[][] mat = new int[n][n];
// 定义初始值 num = 1,循环终止值 tar = n * n
int num = 1;
int tar = n * n;
// 当 num <= tar 时,始终按照 从左到右、从上到下、从右到左、从下到上 填入顺序循环
while (num <= tar) {
// left to right
for (int i = l; i <= r; i++) {
mat[t][i] = num++;
}
t++; // 更新上边界,内缩1个单位
// top to bottom
for (int i = t; i <= b; i++) {
mat[i][r] = num++;
}
r--; // 更新右边界,内缩1个单位
// right to left
for (int i = r; i >= l; i--) {
mat[b][i] = num++;
}
b--; // 更新下边界,内缩1个单位
// bottom to top
for (int i = b; i >= t; i--) {
mat[i][l] = num++;
}
l++; // 更新左边界,内缩1个单位
}
return mat;
}
}
拓展题目:
54.螺旋矩阵
剑指Offer 29.顺时针打印矩阵
数组专题总结
这个专题目前做了5题,涉及到了几种常见思路和方法:
- 二分法;(循环不变量原则:只有在循环中坚持对区间的定义,才能清楚的把握循环中的各种细节)
- 双指针法;(快慢指针法:通过一个快指针和慢指针在一个for循环下完成两个for循环的工作;相向双指针)
- 滑动窗口法;(重点是理解滑动窗口如何移动窗口起始位置,并达到动态更新窗口大小的条件)
- 模拟法;(真正解决题目的代码都是简洁的,或者有原则性的)
为了更好地写代码和保留自己的做题轨迹,我再本地IDE上专门建立了一个代码随想录的代码文件夹,里边会存放着未来2个月的刷题代码和注释,如下图所示。这样好处很多,也推荐给大家。之后二刷或者有时间就会把涉及到的那些拓展题也一并做了,加油!