2023年3月2日 周四
2 第一章数组
977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II ,总结
建议大家先独立做题,然后看视频讲解,然后看文章讲解,然后在重新做一遍题,把题目AC,最后整理成今日当天的博客
拓展题目可以先不做
详细布置
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
【第一想法与实现(困难)】
-
暴力解法,直接平方之后使用标准库自带的std::sort快排
-
双指针,写起来也不麻烦,然后我自己会一个个emplace_back之后最后std::reverse一下。自己写的双指针时间空间都还不错,80~90
【看后想法】
-
暴力解法,甚至连新数组也不申请,直接修改了原数组…
-
不需要std::reverse,只要一开始初始化固定size,都给0就行了。然后从后往前幅值
【实现困难】
重写基本比较容易,哈哈
【自写代码】
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
// 双指针
vector<int> ret(nums.size(), 0);
int k = nums.size()-1;
for(int i=0, j = nums.size()-1; i <= j; --k) {
if (nums[i]*nums[i] > nums[j]*nums[j]) {
ret[k] = nums[i]*nums[i];
++i;
} else {
ret[k] = nums[j]*nums[j];
--j;
}
}
return ret;
}
};
【收获与时长】一小时多,复习了双指针。对于循环结束的条件,手动判断一下有没有等号
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,还可以多一个break优化。因为当能更新最短子数组,就说明这个left之下已经取得最小长度了,不需要再变化right,因为虽然满足子数组和更大,但是长度只会更长。
-
INT32_MAX,表示越小越好的整形初始值
-
【看后想法】
滑动窗口类似双指针,只不过关注的是两个指针之间的内容,形成了滑动的“窗口”。关键是for循环的下标是终止的下标而不是初始下标(初始下标就又回到了暴力解法)。
【实现困难】注意需要一直移动左指针,是while不是if。while条件是 >= ,带着等号!
【自写代码】
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
// 滑动窗口,左闭右闭,[i, j],for循环变量为终止下标j
int i = 0;
int sum = 0;
int result = INT32_MAX;
for(int j=0; j < nums.size(); j++) {
sum+=nums.at(j);
std::cout << "i:" << i << ",j:" << j << ",sum:" << sum << std::endl;
// 如果子数组满足大于target条件,一直移动初始下标,看能否缩短
while(sum >= target) {
int sub_length = (j - i + 1);
result = sub_length < result ? sub_length : result;
std::cout << "sub_length:" << sub_length << ",result:" << result << ",sum:" << sum << std::endl;
sum-=nums.at(i);
++i;
}
}
return result == INT32_MAX ? 0 : result;
}
};
【收获与时长】1.5小时,滑动窗口听过很多次,结构巧妙,强!
59.螺旋矩阵II
【链接】(文章,视频,题目)
题目建议: 本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。
题目链接:力扣 文章讲解:代码随想录 视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/
【第一想法与实现(困难)】想直接使用暴力解法,记录当前的left, right, top, bottom的位置。两边闭合区间。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
// 暴力,左闭右闭[left, rigth],上闭下闭[top, bottom]
vector<vector<int>> res(n, vector<int>(n, 0));
int left = 0, right = n-1, top = 0, bottom = n-1;
int count = 0;
int num = 1;
// while(left <= right || bottom <= top) {
while(num <= n * n) {
if(count%4 == 0) { // top行,从左到右,++top
for(int j = left; j <= right; ++j) {
res[top][j] = num++;
}
++top;
} else if(count%4 == 1) { // right列,从上到下,--right
for(int i = top; i <= bottom; ++i) {
res[i][right] = num++;
}
--right;
} else if(count%4 == 2) { // bottom行,从右到左,--bottom
for(int j = right; j >= left; --j) {
res[bottom][j] = num++;
}
--bottom;
} else if(count%4 == 3) { // left列,从下到上,
for(int i = bottom; i <= top; --i) {
res[i][left] = num++;
}
++left;
} else {
std::cout << "error" << std::endl;
}
++count;
}
return res;
}
};
出现问题runtime error:执行出错
Line 1034: Char 34: runtime error: addition of unsigned offset to 0x607000000020 overflowed to 0x607000000008 (stl_vector.h)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/…/lib/gcc/x86_64-linux-gnu/9/…/…/…/…/include/c++/9/bits/stl_vector.h:1043:34
overflowed是数组越界问题,调试方法,一段一段解注释,看哪里的边界条件写错了,最后发现是最后一个for循环的停止条件写错了。<=应为>=。
【看后想法】
而求解本题依然是要坚持循环不变量原则。
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
卡哥的讲解基本与我想的一致,但是感觉左闭右开的写法比较麻烦。我个人感觉卡哥的写法需要处理的特殊情况比较多(奇数偶数,中间剩一个格),代码不够简洁优美。附上修改后自己的代码。
【实现困难】不写了,思路基本正确,我的写法更简洁。
【自写代码】
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
// 暴力,左闭右闭[left, rigth],上闭下闭[top, bottom]
vector<vector<int>> res(n, vector<int>(n, 0));
int left = 0, right = n-1, top = 0, bottom = n-1;
int count = 0;
int num = 1;
while(num <= n * n) {
if(count%4 == 0) { // top行,从左到右,++top
for(int j = left; j <= right; ++j) {
res[top][j] = num++;
}
++top;
}
else if(count%4 == 1) { // right列,从上到下,--right
for(int i = top; i <= bottom; ++i) {
res[i][right] = num++;
}
--right;
}
else if(count%4 == 2) { // bottom行,从右到左,--bottom
for(int j = right; j >= left; --j) {
res[bottom][j] = num++;
}
--bottom;
}
else if(count%4 == 3) { // left列,从下到上,
for(int i = bottom; i >= top; --i) {
res[i][left] = num++;
}
++left;
}
++count;
}
return res;
}
};
【收获与时长】
-
自己通过逐段解注释的方法来debug runtime error,
-
数组,要坚持区间定义,循环不变量!
数组总结
题目建议:希望大家 也做一个自己 对数组专题的总结
文章链接:https://programmercarl.com/%E6%95%B0%E7%BB%84%E6%80%BB%E7%BB%93%E7%AF%87.html
坚持区间定义,循环不变量原则!
二分法——基础的区间定义,左闭右闭,左开右闭
删除元素——快慢指针,分别代表新数组元素(快)与下标(慢);相向双指针,待删除元素与须保留元素。
有序数组的平方——双指针,两头夹到中间
长度最小子数组——滑动窗口,终止位置做for循环变量,while判断而不是if,能短则短
螺旋矩阵——模拟行为。区间定义,循环不变量。模拟4个方向即可,注意不同方向的各种细节各不相同,初始值,终止条件,变化方向。