今日学习时长:3h
977.有序数组的平方
题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/
文章讲解:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html
视频讲解: https://www.bilibili.com/video/BV1QB4y1D7ep
看到这个题,暴力法很好想,直接平方完排序就行。观察平方后的数组,想到了用双指针,但是还是想着27.移除元素里,在一个数组里完成操作。其实可以新开一个数组,直接把比较得到的值放到新数组里。
思路讲解
观察数组,两边大中间小。考虑用双指针(这种从两边到中间汇合的题目 双指针都挺好用的?)i从左边开始,j从右边开始。比较i和j位置上大的数放到结果数组里。
while条件是(i<=j)
,如果没有=,一共只会赋值len-1个,还有一个位置没有赋值。
如果A[i] * A[i] < A[j] * A[j]
那么result[k--] = A[j] * A[j];
。
如果A[i] * A[i] >= A[j] * A[j]
那么result[k--] = A[i] * A[i];
。
这里最好在比较的时候才平方,不要先遍历一遍数组变成平方的,这样会快一点。
class Solution {
public int[] sortedSquares(int[] nums) {
int len = nums.length;
for(int i = 0; i < len; i ++) {
nums[i] = nums[i] * nums[i];
}
int[] res = new int[len];
int i = 0,j = len - 1;
int k = len - 1;
while(i <= j) {
if(nums[i] > nums[j]) {
res[k--] = nums[i++];
} else if (nums[i] <= nums[j]) {
res[k--] = nums[j--];
}
}
return res;
}
}
感想
这题比较简单,记得分析数组元素的特征,如果有两边到中间的趋势,考虑用双指针。Java中平方不能用^!!!
209.长度最小的子数组
题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/
视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE
第一思路是暴力,遍历数组,把每个位置当做起始位置往后累加找子数组,并记录下当前最小长度len。
思路讲解 滑动窗口
两层for暴力,枚举出所有子数组长度,时间复杂度O(n^2)
。(超时了)
滑动窗口的方式 相当于用一层for做了两层for的事情。
- 关键点:
for循环里的j
代表的是滑动窗口的起始位置还是终止位置?
先假设j代表的是起始位置。依然要让终止位置往后遍历,计算s,和两层for没什么区别。
- 所以for循环里的j 代表的一定是终止位置!!!。起始位置需要用动态移动的策略。
滑动窗口最精髓的思路在于如何调整起始位置!
滑动窗口里元素的和sum
,移动终止位置的同时累加sum,sum += nums[j]
- 如何动态调整起始位置?当终止位置移动到
sum ≥ s
了之后,再去移动起始位置。同时记录下滑动窗口长度subL。
持续更新最小值result = min(result, subL)
。(result初始为最大值即整个数组的长)
然后sum -= nums[i]; i++;
,移动起始位置,同时更新sum
___ (sum >= target)
这里循环条件应该用if还是while?
如果写if的话,可能有这种情况:j移动到sum>=s
停住,此时进入if,**if只会移动一次i,j就又开始移动了!**实际上i需要连续移动来更新滑动窗口大小,因此得用while。让起始位置连续的向后移动直到sum >= target
!
- 最后返回结果时,注意要考虑整个数组的和还是比target小的情况。
解决方法:
if(i == 0) return 0;
如果起始位置一直没有动过,就说明一次while都没进入过,i也就为0。
注意,if(res == len) return 0;
不行 可能有这种情况:target = 15 nums=[1,2,3,4,5]
而如果用上面的逻辑,有进过一次while,i更新了。
- 或者
res初始赋值Integer.INT_MAXVALUE
,如果到最后都没变过,说明不满足return result == Integer.MAX_VALUE ? 0 : result;
完整代码:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int res = Integer.MAX_VALUE; //返回值,子数组长度初始化为最大值
int i = 0,sum = 0,subL = 0; //i为起始位置
for(int j = 0; j < nums.length; j++) { //j为终止位置
sum = sum + nums[j]; //累加sum
while(sum >= target) {
subL = j - i + 1;
res = Math.min(subL, res);
sum = sum - nums[i++];
}
}
return res == Integer.MAX_VALUE ? 0 : res;
}
}
总结
计算数组长度要用j-i+1
滑动窗口精髓在于遍历的是终止位置,动态移动起始位置。同时更新子数组长度和结果
动态更新结果可以用res = Math.min(res, subL)
记得考虑整个数组的和仍比target小的情况!
59. 螺旋矩阵 II
题目链接:https://leetcode.cn/problems/spiral-matrix-ii/
文章讲解:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html
视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/
碰撞检测解法
class Solution {
public int[][] generateMatrix(int n) {
int[] dx = {-1,0,1,0};
int[] dy = {0,1,0,-1}; //上右下左4方向偏移量
int[][] res = new int[n][n];
int a = 0,b = 0,x = 0,y = 0;
int d = 1; //初始方向向右
for(int i = 1; i <= n*n; i++) {
res[x][y] = i; //在当前位置填值
a = x + dx[d];
b = y + dy[d];
if(a < 0 || a >= n || b < 0 || b >= n || res[a][b] > 0) { //越界条件
d = (d + 1) % 4; //更新方向
a = x + dx[d];
b = y + dy[d];
}
//此时才真正赋值
x = x + dx[d];
y = y + dy[d];
}
return res;
}
}
模拟解法
定义好循环不变量左闭右开
,每条边最后一个节点留给下一条边处理
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
loop = n / 2; //每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
Java代码:
class Solution {
public int[][] generateMatrix(int n) {
int loop = 0; // 控制循环次数
int[][] res = new int[n][n];
int start = 0; // 每次循环的开始点(start, start)
int count = 1; // 定义填充数字
int i, j;
while (loop++ < n / 2) { // 判断边界后,loop从1开始
// 模拟上侧从左到右
for (j = start; j < n - loop; j++) {
res[start][j] = count++;
}
// 模拟右侧从上到下
for (i = start; i < n - loop; i++) {
res[i][j] = count++;
}
// 模拟下侧从右到左
for (; j >= loop; j--) {
res[i][j] = count++;
}
// 模拟左侧从下到上
for (; i >= loop; i--) {
res[i][j] = count++;
}
start++;
}
if (n % 2 == 1) {
res[start][start] = count;
}
return res;
}
}
数组总结
这里两道题用到了循环不变量区间的定义。以及两个双指针,双指针的目的就是在一层for里做两层for的事情。滑动窗口也很巧妙,精髓在于遍历的是终止位置,如何动态移动起始位置。
螺旋矩阵我先用的碰撞检测法,4个方向偏移量,感觉比较容易理解。
要多考虑几种情况,比如209题,一开始忘记考虑整个数组加起来还是比Target小的情况,采用赋值成INT_MAX来判断是不是这样。
这两天收获满满,希望继续坚持!