代码随想录算法训练营
Day2 代码随想录算法训练营第二天 |LeetCode977.有序数组的平方 ,LeetCode209.长度最小的子数组 ,LeetCode59.螺旋矩阵II ,总结
文章目录
前言
代码随想录算法训练营第二天
主要内容是双指针,滑动窗口,模拟
一、LeetCode977.有序数组的平方
1 思路
(1)特殊之处
1)nums是升序的
2)结果要求是升序的
3)在平方时,以0为分界,结果是先增后减的
(2)做法解析:双指针是要点
1)先考虑平方以后怎样获得升序数组
既然平方的结果先増后减,那么我们考虑使用归并的思想,用两个指针对左右两侧(正负两侧)进行遍历,比较大小。
在这里用i遍历正数和0的部分,用j遍历负数部分
nums[i]*nums[i]> nums[j]*nums[j] | nums[i]*nums[i]<= nums[j]*nums[j] |
---|---|
选择 j 所代表的平方结果,j 移动, i不动 | 选择 i 所代表的平方结果,i 移动,j不动 |
2)接下来我们分析指针具体怎样移动
首先找出正负的分界:让i先移动,从左向右遍历,遇到第一个非负的数,break跳出循环
接着找到j的起点:此时i指向第一个非负数,也就是平方值最小的非负数,j=i-1,使得j从平方值最小的负数开始,i和j从中间向两边遍历。i递增,j递减。
(3)细节
1)j何时赋值
2)循环的边界
在最开始我用i < n || j >= 0作为边界,在跳出循环后单独处理没有到头的那一端
然而循环条件为i < n || j >= 0更好:
把对某一方遍历完而另外一边没有完成的情况放在循环里,而不是单独讨论,单独讨论可能缺项。
这样的做法可以不必单独处理只有正或者只有负的情况,因为这两种是极端的正/负一方提前用完,而另外一方还有剩余的情况
3)何时平方
我的做法与官方题解在平方的处理上不同。
官方的每次比较都会进行一次平方运算,最终压入结果vector时还要再算一次平方。
我的做法中,在遍历寻找正负分界的过程(for循环)中进行平方并存放在nums中,这样可以得到所有负数和第一个非负数的平方。在双指针比较(while循环)时,每次更新i,都计算nums[i]的平方并赋值给nums[i]。
这样比较和结果赋值都用nums即可。
2 题解
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int i = 0;
int j = 0;
int n = nums.size();
vector<int> a;
for (i = 0; i < n; i++) {
if (nums[i] >= 0) {
nums[i] *= nums[i];
break;
}
nums[i] *= nums[i];
}
j = i - 1;
while (i < n || j >= 0) {
if (i == n) {
a.push_back(nums[j]);
j--;
} else if (j < 0) {
a.push_back(nums[i]);
i++;
if (i < n)
nums[i] *= nums[i];
} else if (nums[j] < nums[i]) {
a.push_back(nums[j]);
j--;
} else {
a.push_back(nums[i]);
i++;
if (i < n)
nums[i] *= nums[i];
}
}
return a;
}
};
二、LeetCode209.长度最小的子数组
题目链接:LeetCode209.长度最小的子数组
1.滑动窗口法
滑动窗口是双指针的一种。
(1)指针i,j分别表示子数组的终点和起点,先移动i,发现子数组长度大于target后再左移j
(2)每次只要子数组长度大于target就保存答案
(3)子数组长度小于target后,向左移动i
2.细节
何时进行sum+=nums[i]:在while循环之前,如果在循环之后,那么i=n-1时,求和后i变为n,跳出for循环,漏掉i=n-1的情况
如何保存答案:每次只要子数组长度大于target,就要将现在的子数组长度和现有的答案进行比较,保存较小的
len的初值:由于更新方式是每次取小的值作为结果,那么len初值需要大于任何一个可能的子数组长度。子数组长度最大为n,所以len初值大于n
3.题解
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i = 0; // 子数组终点
int j = 0; // 子数组起点
int n = nums.size();
int len = n + 1;
int sum = 0;
for (i = 0; i < n; i++) {
sum += nums[i];
while (sum >= target) {
len = min(len, i - j + 1);
sum -= nums[j];
j++;
}
}
if (len > n)
return 0;
else
return len;
}
};
三、LeetCode59.螺旋矩阵II
题目链接:LeetCode59.螺旋矩阵II
1.方法
这道题主要进行模拟,用到了前面二分法对区间的定义
模拟时注意同统一区间
2.思路
(1)将整个过程分成一圈一圈的,总圈数n/2
(2)将一圈分为四条边,每条边左开右闭,如表所示
上 | 上 | 右 |
---|---|---|
左 | 右 | |
左 | 下 | 下 |
(3)实现每一条边
1)第i圈,每条边长度为n-(2*i-1)
2)每条边的填充方式
上:y++
右:x++
下:y–
左:x–
3)填充的数由不断增加的count实现
3.题解
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> ans(n, vector<int>(n, 0));
ans[0][0] = 1;
int startx = 0;
int starty = 0;
int x = 0;
int y = 0;
int loop = n / 2; // 一共多少圈
int len; // 每条边的长度都相等 len=n-(2*i-1)(第i圈)
int count = 1;
for (int i = 1; i <= loop; i++) {
x = startx;
y = starty;
len = n - (2 * i - 1);
for (int j = 0; j < len; j++, y++) {
ans[x][y] = count;
count++;
}
for (int j = 0; j < len; j++, x++) {
ans[x][y] = count;
count++;
}
for (int j = 0; j < len; j++, y--) {
ans[x][y] = count;
count++;
}
for (int j = 0; j < len; j++, x--) {
ans[x][y] = count;
count++;
}
startx++;
starty++;
}
if (n % 2)
ans[n / 2][n / 2] = count;
return ans;
}
};
总结
数组的内容到这里告一段落
数组的主要内容有
1、二分法
(1)两种不同的二分法写法
(2)二分思想的应用:处理单调递增或单调递减的问题,例如求满足条件的最小结果
2、双指针:
两个指针各自负责一部分
通过一个快指针和慢指针在一个for循环下完成两个for循环
3、滑动窗口
先移动子数组的终点指针,发现子数组和>=target以后移动子数组起点指针,每移动一次起点指针,都要更新一次结果,直到子数组和<target,再更新重点指针
4、模拟
(1)模拟需要确定循环不变量,在整个过程中用统一的区间定义,统一的转移方式
(2)模拟需要拆解问题,问题中相似的过程归为一类,先分析类似问题中的一个,用循环实现