题目建议: 本题关键在于理解双指针思想
题目链接: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
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(), 0);
int low = 0, high = nums.size() - 1;
int idx = high;
while (low <= high) {
if (nums[low] * nums[low] >= nums[high] * nums[high]) {
result[idx--] = nums[low] * nums[low];
low++;
}
else {
result[idx--] = nums[high] * nums[high];
high--;
}
}
return result;
}
};
看到题目首先想到的是暴力遍历,但是效率太低,是O(n + log n), 第一个n是算平方,第二个logn是排序,是不可接受的,不满足题目要求。所以需要用双指针法,因为原数组是升序的,只有头尾可能有最大值,所以需要头尾双指针向内收缩,比较后插值,
时间复杂度为O(n),
空间复杂度为O(n)
其中有几个遗忘的点在于vector的初始化可以通过初始函数实现,nums(size,0),其中size是元素数量,0为设定的初始值;以及c++中vector的末尾元素添加用的是push_back(), 不是python的append函数
题目建议: 本题关键在于理解滑动窗口,这个滑动窗口看文字讲解 还挺难理解的,建议大家先看视频讲解。 拓展题目可以先不做。
题目链接:
文章讲解:
视频讲解:
class Solution {
public:
// 这个双指针滑动窗口的意义是在每一个右边界时找到最大左边界,即子数组结尾元素固定时,找最短的头元素
int minSubArrayLen(int target, vector<int>& nums) {
// int left, right, sum = 0, result = INT_MAX;
// for (left = 0, right = 0; right < nums.size();right++) {
// sum += nums[right];
// while (sum >= target) {
// result = result < (right - left + 1) ? result : (right - left + 1);
// sum -= nums[left];
// left++;
// }
// }
// return result == INT_MAX ? 0 : result;
int left = 0, right = 1, sum = nums[0], result = INT_MAX;
for (right = 1; right <= nums.size();right++) {
while (sum >= target) {
result = result < (right - left) ? result : (right - left);
sum -= nums[left];
left++;
}
if (right < nums.size()) {
sum += nums[right];
}
}
return result == INT_MAX ? 0 : result;
}
};
第一个想到的是双侧循环,暴力求解,但时间复杂度是O(n^2),题目要求O(n),所以不可行,所以用双指针滑动窗口,而由于该题中的原数组是无序的,所以在每次改变窗口大小时,需要右一个固定边界,以固定边界作为遍历的标志。在这里需要求最小子数组长度,所以以右侧边界为遍历标志。
实质上本题的双指针窗口的意义是在每一个右边界时找到最大左边界,即子数组结尾元素固定时,找最短的头元素
时间复杂度O(n)
空间复杂度O(1)
尾减头长度计算需要记得+1,整数最大值是INT_MAX,三目条件运算符格式例如取最大:x= x>y?x:y
如果头尾两个while循环,是需要数组事先排序,如升序排序下,和大了就缩小左边界,和小了就放大右边界。
题目建议:本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。
题目链接:
文章讲解:
视频讲解:
自己写的
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0));
int count = 1, i, j, loop;
for (i = 0, j = 0, loop = 0; loop < n / 2; i++, j++, loop++) {
while (j < n - 1 - loop) res[i][j++] = count++;
while (i < n - 1 - loop) res[i++][j] = count++;
while (j > loop) res[i][j--] = count++;
while (i > loop) res[i--][j] = count++;
}
if (n % 2) res[loop][loop] = count;
return res;
}
};
代码随想录给的
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0));
int startx = 0, starty = 0;
int loop = n / 2;
int mid = n / 2;
int count = 1;
int offset = 1;
int i, j;
while (loop --) {
for (j = starty; j < n - offset; j++) {
res[startx][j] = count++;
}
for (i = startx; i < n - offset; i++) {
res[i][j] = count++;
}
for (; j > starty; j--) {
res[i][j] = count++;
}
for (; i > startx; i--) {
res[i][j] = count++;
}
startx++;
starty++;
offset++;
}
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
评论老哥给的
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int t = 0; // top
int b = n-1; // bottom
int l = 0; // left
int r = n-1; // right
vector<vector<int>> ans(n,vector<int>(n));
int k=1;
while(k<=n*n){
for(int i=l;i<=r;++i,++k) ans[t][i] = k;
++t;
for(int i=t;i<=b;++i,++k) ans[i][r] = k;
--r;
for(int i=r;i>=l;--i,++k) ans[b][i] = k;
--b;
for(int i=b;i>=t;--i,++k) ans[i][l] = k;
++l;
}
return ans;
}
};
最简单的一道题竟然搞了非常久的时间,主要的原因在于除以2的时候,应当是 / 2,顺手用了python的 // 2,想整除,结果忘了在C++里面是注释,而颜色又不显眼,导致始终没发觉错误,耽搁了非常长的时间,以为是夹杂了哪个中文全角符号,排查了很久。
代码随想录的代码,和评论老哥的代码可以看做是两个习惯了,第三种代码的习惯更贴近我的思想,只是我想尽可能节省自定义符号,所以用了loop来写,需要稍微绕一点点,也无法避免最后奇数情况下对中央的补充,所以还是第三种好一些。
当然第二种代码思路非常清晰,每次循环的起点,变量与不变量,过程,循环次数很分明,可以作为思想模板。
时间复杂度O(n^2)
空间复杂度O(1)
vector建立双层数组:
vector
c++注释是 // 不要顺手用了
对于边界,还是建立专门的变量保存比较方便控制。