代码随想录训练营D2-数组篇 day2| 977 有序数组的平方、209 长度最小的子数组 、59.螺旋矩阵II
代码随想录训练营-数组篇 day2| 977 有序数组的平方、209 长度最小的子数组 、59.螺旋矩阵II
(一) 977 有序数组的平方
1. 思路
1.1 直观思路
循环一遍,在原数组中将元素平方并放回原处。使用Arrays.sort(),将数组由小到大排序。
1.2 代码随想录思路
应用双指针思想。
由于原数组是有序的,从小到大。如果有正、有负,那么在平方完之后,数组呈两边大,中间小的状态。
可以在数组头尾分别设置一个指针,两指针向中间逼近,两个指针对应元素的平方进行比较,较大的元素的平方存入新数组。由于新数组要求也是从小到大,所以可以从后向前存入新数组中。指针所指元素进新数组,那么指针也移动一格(前面的指针向后一格,后面的指针向前一格)
2. 代码
public class Q977SortedSquares {
//给你一个按 非递减顺序 排序的整数数组 nums,
//返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
class Solution {
public int[] sortedSquares(int[] nums) {
int len = nums.length;
for(int i = 0; i < len; ++i){
nums[i] *= nums[i];
}
Arrays.sort(nums);
return nums;
}
//双指针。
//由于原本数组是有序的,在平方之后,数组中元素分布是两边大中间小。
//可以设置两个指针分别在开头和结尾的位置,每次都选择当前剩余数组中最大的元素放到新数组中
//大 <--- 小 ----> 大
//^ ^
//p q 两个指针依次向中间逼近
//新数组也是从小到大排列的,所以填充新数组时 要从后向前填充。
public int[] sortedSquares1(int[] nums) {
int len = nums.length;
int[] result = new int[len];
//计算平方 以及 排序 可以同时进行
int index = len - 1;
for(int p = 0, q = len -1; p <= q;){
//在两指针处,选择较大的,在新数组中从后向前填充
if(nums[p] * nums[p] >= nums[q] * nums[q]){
//为新数组赋值,并且p后移
result[index--] = nums[p] * nums[p];
++p;
}else {
//为新数组赋值,并且q前移
result[index--] = nums[q] * nums[q];
--q;
}
}
return result;
}
}
}
3. 实现过程中的问题
(二) 209.长度最小的子数组
题目建议: 本题关键在于理解滑动窗口。
1. 思路
1 双层for循环,遍历出所有的子数组,并随时更新最小子数组。
2 双指针,这里也叫滑动窗口。2个指针。
因为要找的子数组是连续的,所以可以用一个前后都可以伸缩的框来挑出最小子数组。
起始时,框的左右两端都指向下标0处元素。框的右侧先行,整个框不断变大,变量sum要累加框中的元素,直到sum>=target, 此时框中的算是疑似长度最小子数组(没遍历完不能确定谁最小),所以更新最小子数组长度min;
这时看看框的长度能否更小,所以将框的左侧向右压,若还满足sum>=target,则更新min,直到sum <target就不压了,转而右移框的右侧,筛选其他可能的子数组。
2. 代码
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int sum = 0;
int min = Integer.MAX_VALUE;//>=target 的最小子数组的长度
for(int right = 0; right < nums.length; ++right){
//每一轮for循环 代表右侧又新加了一个元素,先更新sum
sum += nums[right];
//sum >= target ,先更新min,再不断!收缩左侧
while(sum >= target){//!!!这里不需要 left <= right
min = Math.min(right - left + 1, min);
//左侧收缩,并更新sum
sum -= nums[left++];
}
//sum < target 右侧向右拓宽,即进入下一轮for循环
}
//若min从来没有更新过,则返回0
return min == Integer.MAX_VALUE ? 0 : min;
}
3. 实现过程中的问题
//为什么while中不需要呢 && left <= right
while(sum >= target){
min = Math.min(right - left + 1, min);
//左侧收缩,并更新sum
sum -= nums[left++];
}
思考极限情况left一直++,直到与right相遇在同一位置。
所以极限时也不会出问题。
(三) 59.螺旋矩阵II
题目建议: 本题关键还是在转圈的逻辑,以及二分搜索中提到的区间定义(左闭右闭、左闭右开,选择了一种后 从前到后就要一致)。
0.题目
给你一个正整数 n ,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
1. 思路
1.1 我狄思路
1.创建nxn二维数组,遍历1-n^2并填充至二维数组
2.分类讨论:
i横向下标,j纵向下标
随着数字不断地填充,横纵坐标所能走到的最小、最大值都会发生变化。
iMax:横坐标所能达到的最大值。其他同理。
(以下- - iMax 均显示为–iMax)
1)向右侧走时。先++iMin,++j(考虑走第二圈时,要先向右,因为原地的位置已经填好值了),按顺序向右填充(i值不变,j不断增大),直到j达到最大值jMax时,则要向下填充。
2)向下填充时。先–jMax,++i(i+1 即向下串一个位置),按顺序向下填充(j不变,i增大),直到达到i最大值iMax,开始向左填充。
3)向左侧走时。先–iMax,–j(j-1 即j向左串一个位置) 按顺序向左填充(i不变,j不断变小),直到j达到最小值jMin时,则要向上填充。
4)向上填充时。先++jMin,–i(i-1 向上串一个位置)按顺序向上填充(j不变,i不断变小),直到达到i最小值iMin,开始向右填充。
直到填充至n^2
1.2 代码随想录思路
整体也是分为四步:左->右,上->下,右->左,下->上。
只是while循环中条件改为判断需要走几圈(一圈包括上面四步)。
(n = 2时,一圈;n = 4时,两圈;n为偶数时,刚好整数圈走完;
n = 3时,一圈 + 填充最中间的元素;n为奇数时, 须最后补充)
区间定义的一致性体现在,每一步中都是左闭右开。
即,第一步,左->右,填充A而不填充B;第二步 上->下,填充B而不填充C…
2. 代码
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int iMin = 0, jMin = 0, iMax = n - 1, jMax = n - 1;
int cur = 1;//待填入数组中的数值
int i = 0, j = -1;//工作下标
while(cur <= n * n){
/*
i横向下标,j纵向下标
1)向右侧走时。先++iMin,按顺序向右填充(i值不变,j不断增大),直到j达到最大值jMax时,则要向下填充。
2)向下填充时。先--jMax,按顺序向下填充(j不变,++i),直到达到i最大值iMax,开始向左填充。
3)向左侧走时。先--iMax,按顺序向左填充(i不变,--j),直到j达到最小值jMin时,则要向上填充。
4)向上填充时。先++jMin,按顺序向上填充(j不变,--i),直到达到i最大值iMin,开始向右填充。
直到填充至n^2
* */
++iMin;
while (j < iMax){
result[i][++j] = cur++;
}
--jMax;
while(i < iMax){
result[++i][j] = cur++;
}
--iMax;
while(j > jMin){
result[i][--j] = cur++;
}
++jMin;
while (i > iMin){
result[--i][j] = cur++;
}
}
return result;
}
/**n值 圈数
* 1 0圈 & 在最终间填上数1
* 2 1圈
* 3 1圈 & 在最终间填上数9
* 4 2圈
*/
public int[][] generateMatrix1(int n) {
int[][] result = new int[n][n];
int x = 0, y = 0;//下标
int offset = 0;//约等于 现在是第几圈。用于限制坐标范围,循环完一圈 就++
int cur = 1;//待填入数组中的数值
//无论是奇偶,都先走n/2圈
while(offset++ < n/2){
//以下四步 每步都是左闭右开
//左-->右
while(y < n - offset){
result[x][y++] = cur++;
}
while(x < n - offset){
result[x++][y] = cur++;
}
while(y > offset - 1){
result[x][y--] = cur++;
}
while(x > offset - 1){
result[x--][y] = cur++;
}
x++;//此为下一圈的起始位置
y++;
}
//n为奇数时,最终间补一个值
if(n % 2 == 1){
result[x][x] = cur;
}
return result;
}
3. 实现过程中的问题
边界问题。最终是debug de好的边界。