第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
/**
* 2023/12/28 第二天 T1
* 977.有序数组的平方
*/
//暴力: o(n*logn)
public int[] sortedSquares(int[] nums) {
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
//双指针 o(n)
public int[] sortedSquares2(int[] nums) {
int i = 0,j = nums.length - 1,k = nums.length - 1;
int[] newNums = new int[nums.length];
for (i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
i = 0;
//双指针(类似归并中合并两个有序序列)
while (i <= j){ //要带等于,否则会漏掉一个元素!
if (nums[i] > nums[j]) newNums[k--] = nums[i++];
else newNums[k--] = nums[j--];
}
return newNums;
}
- 看到题目就想先平方求新数组,然后新数组调Arrays.sort(newNums) 进行快排,时间复杂度是o(n + n*logn)
- 看了双指针解法,我的理解类似于归并中合并两个有序数组,由于数组两边的元素的值的平方的值是最大的(有负数时),由两边向中间值是递减的。因此可以使用双指针i j,用i指向最左边的元素,j指向最右边的元素,i右移,j左移。开辟一个和旧数组大小一样新数组,k指向新数组的最右边即最大的元素位置。旧i j元素比较平方大小,大的加入新数组,然后移动。循环边界条件是i <= j。注意要加上等号,否则会漏掉一个元素。
- 收获:对双指针有了一定的理解,1 h。
/**
* 2023/12/28 第二天 T2
* 209.长度最小的子数组
*/
//暴力 o(n^2) 超时!!!
public int minSubArrayLen(int target, int[] nums) {
int minL = nums.length + 1,sum,l;
for (int i = 0; i < nums.length; i++) {
sum = 0;
l = 0;
for (int j = i; j < nums.length; j++) {
sum += nums[j];
l++;
if (sum >= target){//满足条件 更新最小值 跳出该层循环
if (l < minL) minL = l;
break;
}
}
}
if (minL == nums.length + 1) return 0;
return minL;
}
//滑动窗口 o(n)
public int minSubArrayLen2(int target, int[] nums) {
int i = 0, j = 0,sum = 0,minL = nums.length + 1,l = 0;
//i j 是起始和终止指针,sum是窗口内元素和保证要大于target的最小数组和
for (j = 0; j < nums.length; j++){
sum += nums[j];
while (sum >= target){
l = j - i + 1;//窗口长度
minL = Math.min(minL,l);
sum -= nums[i++];//精髓 i右移 sum的值减去i位置的值,滑动窗口缩小。
}
}
return minL == nums.length + 1 ? 0 : minL;
}
-
第一想法是两层for暴力。
-
滑动窗口的思路(3个要点):
1.保持窗口内数值总和大于或者等于target,且长度是由i起始位置开始的最小的连续子数组。
2.移动窗口起始位置如何移动?当前窗口的值大于target,窗口就向前移动,i右移动,也就是窗口缩小了。
3.移动窗口的终止位置如何移动?窗口的结束位置就是最外层for循环遍历数组的j指针。
-
收获:对滑动窗口有了初步的理解,1.5 h。
/**
* 2023/12/28 第二天 T3
* 59.螺旋矩阵II
*/
public int[][] generateMatrix(int n) {
int[][] nums = new int[n][n];
int startX = 0,startY = 0,offset = 1,count = 1,loop = n / 2;
int i,j;
while (loop-- > 0){
i = startX;
j = startY;
for(j = startY; j < startY + n - offset; j++){
nums[i][j] = count++;
}
for (i = startX; i < startX + n -offset; i++){
nums[i][j] = count++;
}
for (; j > startY; j--){
nums[i][j] = count++;
}
for (; i > startX; i--){
nums[i][j] = count++;
}
startX++;
startY++;
offset += 2; // 边长少2
}
if (n % 2 != 0) nums[startX][startY] = count;
return nums;
}
-
没有思路。。
-
模拟解法思路:
首先确定循环不变量,即每次循环就是四个边,每个边的的遍历赋值都采用左闭右开的方式。
其次确定循环的起点坐标为(startX,startY)。确定边长的遍历边界,如第一条边 j < startY + n - offset。我的理解是startY + n - offset就是就是该边的最后一个元素的下标,而我们需要满足的是左闭右开,因此该下标就不能遍历,也就是 j < startY + n - offset。其中最后一个元素的下标等于 起始下标(startY)+边长长度(n - offset)。offset每循环一次会增加2,相当于边长减少2。
-
收获:模拟算法有了初步的理解,2 h。