LeetCode 977.有序数组的平方
题目链接:977.有序数组的平方
踩坑:这道题当然可以暴力解决,即遍历数组平方后再排序。但是官方也给出了进阶要求(O(n)),代表着对我们的挑战(怎么能认输呢),卡哥也提示了要使用双指针,但是做题的惯性使我陷入了27.移除元素的漩涡,还是想原地解决而不申请新的空间(还是太年轻)。
思路:可以先对数组进行平方,也可以不做,因为只要考虑的是绝对值,原数组都会是[大,小,大]的结构,其中最大值一定在头部或尾部。那我们就可以申请一个新的数组,把原数组中的元素从大到小选择出来,顺序放入新数组,而这个选择的过程可以用到双指针。
代码:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(), 0);
int idx = nums.size() - 1;
for(auto i = nums.begin(); i != nums.end(); i++)
{
*i = (*i) * (*i);
}
for(int i = 0, j = idx; i <= j;)
{
if(nums[i] < nums[j]) result[idx--] = nums[j--];
else if(nums[i] > nums[j]) result[idx--] = nums[i++];
// 注意在双指针指向的元素相同时,需要分别考虑,这涉及到向新数组中添加几个数
else
{
if(i != j)
{
result[idx--] = nums[i++];
result[idx--] = nums[j--];
}
else
{
result[idx--] = nums[i++];
}
}
}
return result;
}
};
LeetCode 209.长度最小的子数组
题目链接:209.长度最小的子数组
踩坑:这道题同样可以使用双循环暴力解决,但是同样有进阶要求(“如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。”),这忍不了一点。滑动窗口这个方法其实很容易想到,但是对于不熟悉的人来说,容易陷入一个误区就是,窗口大小固定,位置滑动,或者是头部固定,尾部滑动。这样就不得不对数组进行多次遍历。
思路:这道题的核心是当窗口扩大到已经大于等于target之后就没必要再扩大了,同时也意味着以当前位置开头的窗口已经可以结束了,应该更换头部,这样就把本需要一次完整遍历的工作拆解了。而头部的更新也需要注意应该一直更新到窗口内sum的小大小于target。因为如果只向后更新一位,而此时窗口内的sum依然大于等于target的话则与核心同理。所以最后就变成了“窗口尾部先变,头部再变”。
代码:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int i = 0;
int sum = 0;
int result = nums.size() + 1;
for(int j = 0; j < nums.size(); j++)
{
sum = sum + nums[j];
while(sum >= target)
{
result = min(result, j-i+1);
// 注意更新头部后的sum
sum = sum - nums[i++];
}
}
// 返回值也要特别考虑,要符合题目要求
if(result == nums.size() + 1) return 0;
else return result;
}
};
LeetCode 59.螺旋矩阵||
题目链接:59.螺旋矩阵||
踩坑:这题的细节非常多,不管是我自己的方法还是卡哥的方法,都需要考虑很多的细节。我自己的方法卡在了如何让每次循环的动作与上一次保持一致直到无法继续,否则就会变成以右,下,左,上为优先级选择前进方向。之后才想到循环不就是做这个的嘛。另外,就是要明确对于每一个位置应该先确认,再填值,这样代码的思路不会乱掉。但是我自己的方法超时了(有点奇怪),但是角度我认为还是非常值得思考的。卡哥的方法在于涉及众多变量,我稍微精简了一下,但最后还是漏了将 i 和 j 重新赋值。
思路:
- 我自己的方法主要在于分析螺旋的逻辑,即右,下,左,上循环,每个动作都尽可能做,直至越界或者有值。比较重要的就是对于每一次前进,都应该先确认能填值再填,不能填就换方向。虽然超时了,但是我觉得值得分享。
- 卡哥的思路就更加一板一眼一些,以转一圈为一次循环,分别为四条边赋值,每次只需要确定初始位置并且过程中注意边界即可。
代码:
// 卡哥的方法
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> result(n, vector<int>(n, 0));
int cnt = 1;
int i = 0, j = 0;
// nn表示圈数,同时也可以在过程中起到缩小边界的作用
for(int nn = 0; nn < n/2; nn++)
{
for(; j < n - nn - 1; j++) result[i][j] = cnt++;
for(; i < n -nn - 1; i++) result[i][j] = cnt++;
for(; j > nn; j--) result[i][j] = cnt++;
for(; i > nn; i--) result[i][j] = cnt++;
i = nn + 1;
j = nn + 1;
}
if(n%2) result[n/2][n/2] = cnt;
return result;
}
};
// 我的方法
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> result(n, vector<int>(n, 0));
int x = 0, y = 0;
int i = 1;
result[x][y] = 1;
while(i <= n*n)
{
// 一直向右
while(y+1 < n && result[x][y+1] == 0) result[x][++y] = i++;
// 一直向下
while(x+1 < n && result[x+1][y] == 0) result[++x][y] = i++;
// 一直向左
while(y-1 >= 0 && result[x][y-1] == 0) result[x][--y] = i++;
// 一直向上
while(x-1 >= 0 && result[x-1][y] == 0) result[--x][y] = i++;
}
return result;
}
};