代码随想录算法训练营第二天| 977.有序数组的平方 、209.长度最小的子数组 、59.螺旋矩阵II
有序数组的平方
题目: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
自己看到题目的第一想法
第一次处理是暴力求解,直接将所有的值都平方之后,做一次冒泡排序,时间复杂度O(n2)。
审题后进阶内容写着用时间复杂度为O(n)的算法来解决本问题,因此排序算法只能通过一次循环去排序。
因为第一天已经学习了双指针法,刚开始没想出怎么写,后面粗看了下随想录题解后自己完成代码。
看完代码随想录之后的想法
这道题的核心还是先理解题目,因为是非递减顺序的整数数组,因此平方之后的数组的最大值要么在数组的第一位,要么在数组的最后一位。
然后再通过双指针法的运用来遍历数组。最后在循环结束时还需要关注下边界值,当前后2个指针合并时,需要将最后一个值放到数组最前面。
----------二刷分割线--------------
*二刷记录,二刷的时候也是用双指针去解答,自己梳理出来的整体的思路如下:nums[right]跟nums[left]进行比较(下面指的都是求平方之后的值),如果nums[right]小于nums[left],则将nums[left]赋值到nums[right]、然后将right的未计算平方后的值赋值到left的位置上,如果大于则将nums[right]值赋值为nums[right]nums[right],然后right–。这里存在一个问题,如果输入是[-5,-3,-2,-1]时,结果为[1,9,4,25]。因此二刷自己的想法错了。错误代码如下:
public int[] sortedSquares(int[] nums) {
// 双指针法去计算
// 因为是非递减的,两边平方求得的值肯定大于中间
// 每次取两头的值计算,大的放到后面并且减减
if(nums.length == 1){
nums[0] = nums[0] * nums[0];
return nums;
}
int left = 0;
int right = nums.length - 1;
while(right >= 0){
int leftMuti = nums[left] * nums[left];
int rightMuti = nums[right] * nums[right];
if(leftMuti > rightMuti){
nums[left] = nums[right];
nums[right] = leftMuti;
}else{
nums[right] = rightMuti;
}
right--;
}
return nums;
}
但是在回顾之前自己写的代码随想录内容时发现看自己写的内容没法回忆起当时的做法,因此还需要再看一遍代码随想录,并且将看完代码随想录后的内容再完善一下。
重看了下代码随想录,代码随想录里新new了一个数组,不在原数组上进行赋值,然后新定义了一个index作为当前赋值的节点,index从后往前。left和right进行比较,谁大就往中间移动,并且为index位置上的数组赋值。
public int[] sortedSquares(int[] nums) {
// 双指针法去计算
// 因为是非递减的,两边平方求得的值肯定大于中间
// 每次取两头的值计算,大的放到后面并且减减
if(nums.length == 1){
nums[0] = nums[0] * nums[0];
return nums;
}
int[] result = new int[nums.length];
int left = 0;
int right = nums.length - 1;
int index = nums.length - 1;
while(left <= right){
int leftMuti = nums[left] * nums[left];
int rightMuti = nums[right] * nums[right];
if(leftMuti > rightMuti){
result[index--] = leftMuti;
left++;
}else{
result[index--] = rightMuti;
right--;
}
}
return result;
}
自己实现过程中遇到哪些困难
看完思路后整体没什么困难,就是写代码的时候还需要注意一些细节,变量写错了导致一次提交没有AC。
长度最小的子数组
209.长度最小的子数组
题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/
文章讲解:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html
视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE
状态:有个想法但是没做出来,后面看随想录。
自己看到题目的第一想法
以为这个方法能使用前缀和的方法进行求值,使用前缀和+双重循环暴力求解,最后没求出来。
----------二刷分割线--------------
二刷记录:二刷的时候想到了滑动窗口,但是代码没写对,后面看了下面“看完代码随想录之后的想法”之后,按照说明写了一下。整体使用for循环和while循环做逻辑处理,外层for循环的递增变量为left:左窗口起始位置。进了循环后,直接while判断sum的值是否大于等于target(这里在循环外要先对sum赋值为nums[0]) ,如果大于则求minLength的值(right - left + 1),移动left(for循环移动)并将sum的值减去nums[left],如果小于,则移动right的位置,累加sum+=nums[right],这里要关注while循环过程中right < nums.length - 1
public int minSubArrayLen(int target, int[] nums) {
if(nums.length == 1){
return nums[0] >= target ? 1 : 0;
}
int minLength = Integer.MAX_VALUE;
int left = 0;
int right = 0;
int sum = nums[left];
// 一个for循环处理求和,一个while循环处理窗口移动
for(; left < nums.length; left++){
while(sum < target && right < nums.length - 1){
right++;
sum += nums[right];
}
if(sum >= target){
minLength = Math.min(minLength,right - left + 1);
}
sum -= nums[left];
}
return Objects.equals(minLength,Integer.MAX_VALUE) ? 0 : minLength;
}
二刷看完代码随想录之后的想法:代码随想录使用的是用right用作for循环,left在while循环内往右移动,一发现sum>target后就往右移动并且sum-=nums[left]。这样的好处就是省去了边界条件的判断,真正实现了for循环内处理求和,while循环内处理窗口移动。
看完代码随想录之后的想法
该题目的核心是理解滑动窗口,定义左右两个位置,通过for循环与while循环体处理逻辑,for循环计算累加值,while循环处理窗口移动。窗口内的数据和大于给定值时改变,求窗口内数组长度。求完长度后再移动左位置,判断移动完的窗口是否还大于给定值。
自己实现过程中遇到哪些困难
看完一遍随想录后自己求解就没什么困难了。关键是想要明白窗口左右移动在代码中如何实现。
螺旋矩阵II
题目: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/
状态:这个基本处于无思路,都是查看代码随想录后写的。看完文字版还没理解透彻,最后通过视频讲解才理解。
自己看到题目的第一想法
第一次是尝试模拟暴力解法,以为用嵌套循环就能处理,后面没有思路直接看答案了。
----------二刷分割线--------------
打开题目的时候瞄了一眼以前写的题解,然后尝试自己开始写。整体也是循环不变量,while循环中,从左到右,从上到下,从右到左,从下到上4个for循环往前推进一步走。还需要定义一个xstart和ystart变量处理每次循环的起始位置以及stop变量处理走到哪里停止。整体思路应该和代码随想录一样,但是自己实现的过程中还是有细节没处理完善,这里给出自己写的时候的代码。
public int[][] generateMatrix(int n) {
// 这个题目核心关注的是循环不变法则
// 走一圈总共走2次x轴 2次y轴
// 先往右走的是x轴,往下走y轴,往左走x轴,往上走y轴
// 1. 根据n的值求出走几圈
// 2. 定义xstart和ystart表示每次走圈时候的初始位置
// 3. 定义中间节点,最中间的节点不用再走,直接赋值
int[][] matrix = new int[n][n];
int loop = n/2;
if(n % 2 == 1){
int middle = n/2;
matrix[middle][middle] = n * n;
}
int xstart = 0;
int ystart = 0;
// stop节点用于停止节点
int stop = 1;
int value = 1;
// 这里关注循环不变量,对照左边的图 每次走2步,所以每次循环要都一样
while(loop-- > 0){
// 往x轴走,往右 走1、走2,xstart到了3
// 1、2
xstart += (stop - 1);
ystart += (stop - 1);
for(;xstart < n - stop;xstart++){
matrix[ystart][xstart] = value++;
}
System.out.println("x->right matrix:" + Arrays.deepToString(matrix));
// 往y轴走,往下
// 3、4
for(;ystart < n - stop;ystart++){
matrix[ystart][xstart] = value++;
}
System.out.println("y->down matrix:" + Arrays.deepToString(matrix));
// 往x轴走,往左
// 5、6
for(;xstart > stop - 1;xstart--){
matrix[ystart][xstart] = value++;
}
System.out.println("x->left matrix:" + Arrays.deepToString(matrix));
// 往y轴走,往上
// 7、8
for(;ystart > stop - 1;ystart--){
matrix[ystart][xstart] = value++;
}
System.out.println("y->up matrix:" + Arrays.deepToString(matrix));
stop++;
}
return matrix;
}
看了代码随想录的代码后:代码随想录内的xstart和ystart是用作每次while循环赋值使用的,for循环的变量使用i和j去执行,使用stop控制每一条边遍历的长度,每次while循环一次后 xstart和ystart以及stop都需要加一。看完后实现的代码:
public int[][] generateMatrix(int n) {
// 这个题目核心关注的是循环不变法则
// 走一圈总共走2次x轴 2次y轴
// 先往右走的是x轴,往下走y轴,往左走x轴,往上走y轴
// 1. 根据n的值求出走几圈
// 2. 定义xstart和ystart表示每次走圈时候的初始位置
// 3. 定义中间节点,最中间的节点不用再走,直接赋值
int[][] matrix = new int[n][n];
int loop = n/2;
int xstart = 0;
int ystart = 0;
// stop节点用于停止节点
int stop = 1;
int value = 1;
// 这里关注循环不变量,对照左边的图 每次走2步,所以每次循环要都一样
int i,j;
while(loop-- > 0){
// 往x轴走,往右 走1、走2,xstart到了3
// 1、2
i = xstart;
j = ystart;
for(;i < n - stop;i++){
matrix[j][i] = value++;
}
System.out.println("x->right matrix:" + Arrays.deepToString(matrix));
// 往y轴走,往下
// 3、4
for(;j < n - stop;j++){
matrix[j][i] = value++;
}
System.out.println("y->down matrix:" + Arrays.deepToString(matrix));
// 往x轴走,往左
// 5、6
for(;i > xstart;i--){
matrix[j][i] = value++;
}
System.out.println("x->left matrix:" + Arrays.deepToString(matrix));
// 往y轴走,往上
// 7、8
for(;j > ystart;j--){
matrix[j][i] = value++;
}
System.out.println("y->up matrix:" + Arrays.deepToString(matrix));
xstart++;
ystart++;
stop++;
}
if(n % 2 == 1){
int middle = n/2;
matrix[middle][middle] = n * n;
}
return matrix;
}
看完代码随想录之后的想法
这道题的提示也是循环不变量,需要有一个模拟过程的思路
就是将每次循环逻辑做模拟,并且得出循环不变量:每次循环走的步长都是一致的,并且下一圈也是相同的执行逻辑。
自己实现过程中遇到哪些困难
自己实现基本上记住了代码随想录里的内容,不过在执行时发现每一次新的循环需要关注第一个节点的位置。i,j的每次循环使用xstart、ystart赋值。
今日收获&学习时长
今天主要是重刷了一次之前写过的代码,整体还算流畅,遇到问题的时候也能通过打印日志去解决。
学习2小时