这3个题前2个还是关于数组中双指针的用法,第3个是二维数组的循环控制。3个题也都提前看过了视频,但还是没有一次性AC,再次说明还不细心,掌握也不扎实。
· 第1道题(977.有序数组的平方)的关键在于,认识到因为数组可能左端是绝对值较大的负数,右边是绝对值较大的正数,所以数组的平方是两边大中间小的。了解这一点后就可以用双指针实现O(n)即用左右两个指针分别从左,右向中心递进,每次都取平方数较大的值新添加进新数组,就可以得到一个非递增的答案,但题目要求的是一个非递减的数组。第1次提交答案错误是由于忘记数组的绝对值是两边大中间小,而非两边小中间大,于是把循环中的第1个if条件错写成l2 < l1;将 < 变为 > 后,结果仍然不对,这是因为忘记了前面提到的题目要求(要求非递增,而不是非递减),于是需要将答案数组的填充改为逆序。然而函数的返回值是vector,自己知道的vector只能正向填充,而又不想将填充完的vector又继续填进新vector,忘记了视频中的做法,于是不会了。经学习得知vector初始化时可以定义大小和初始填充的内容,分别就是前两个参数。也查到另外一种做法就是在得到答案vector后sort()一下,写法为sort(vector.begin(), vector.end()),但如果这样的话sort()的复杂度就已经超过O(n)了。更改后AC。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int l = 0, r = nums.size() - 1;
vector<int> ans(nums.size(), 0);
int ind = nums.size() - 1;
while (l <= r) {
int l2 = nums[l] * nums[l], r2 = nums[r] * nums[r];
if (l2 > r2) {
ans[ind--] = l2;
++l;
}
else {
ans[ind--] = r2;
--r;
}
}
return ans;
}
};
二刷:
用了比较麻烦的方法——初始化要返回的数组为空vector,然后逐个push_back()后再对vector进行reverse()。可以像上面一刷时,初始化要返回的数组时就设置好大小,然后反向填充即可。
第2道题(209.长度最小的子数组)在不止一个笔试题里遇到过类似的题目,看过视频解法看上去简单,实则有很多细节,尤其是在指针右移的时机方面。
- 首先是外部while()循环中的运行条件,需要保证fast指针不越界。
- 其次内部要有两个while(),两者的次序很重要,因为题目需要达到sum >= target的目的,所以应该首先进行fast右移,sum不断增加的循环,达到条件后再,进行slow右移,sum减小的循环。
- 两个内部循环各自的运行条件中,除了要保证sum与target的大小关系外,还要保证指针也不越界(吸取day 1中题目的经验)。
- 每个内部循环中,都应该首先更改sum,再右移指针,而非相反,否则数组中第1个元素会被漏掉。
- 两个内部循环结束后,fast应该处于滑动窗口最右侧+1的位置, Slow应该处于滑动窗口最左侧的位置,所以长度应该是fast - slow + 1。
- 题目要求没找到的情况下返回0,同时又因为需要找长度最小的子数组,所以不能将最小程度初始化为0,解决方法是设定变量find初始化为false,并在函数进入到第2个内部循环后将其设置为true,根据函数是否进入到第2个内部循环来判断是否成功。
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int slow = 0, fast = 0; int sum = 0, ans = INT_MAX; bool find = false; while (fast < nums.size()) { while (fast < nums.size() && sum < target) { sum += nums[fast]; ++fast; } while (slow <= fast && sum >= target) { find = true; sum -= nums[slow]; ++slow; } ans = min(ans, fast - slow + 1); } if (!find) { ans = 0; } return ans; } };
二刷:
- 内层循环的fast右移没写越界判断,导致越界错误;
- 没有写“整个数组的和仍不够大,则需要返回0”的情况;
- 对1的处理错写为“最小长度 == 数组长度”(因为最小长度最长也是“数组长度 - 1”,所以不可能与数组长度想等)。改为“left == 0”后正确如下:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int ans = INT_MAX;
int left = 0, right = 0;
int sum = 0;
while (right < nums.size()) {
while (right < nums.size() && sum < target) {
sum += nums[right++];
}
while (sum >= target) {
sum -= nums[left++];
}
ans = min(ans, right - left + 1);
}
return left == 0 && sum < target ? 0 : ans;
}
};
但如果用一刷时“如果进入slow右移则说明数组和够大”的判断条件会更直观。
第3道题(59.螺旋矩阵II)的关键在于找到循环不变量,这一题的循环不变量在于每个外层循环填充一圈,每个内层循环填充一条边上的n - 1个数字,总共4个内层循环,填充一圈4n - 4个数字。主要问题在于如何设置循环中的数组下标,每个内部循环都是行下标不变,移动列下标,或列下标不变,移动行下标,且移动下标范围的最小值和最大值固定,所以用每次外部循环中设置行起始下标、结束下标,列起始下标、结束下标这4个变量的方式来控制4个循环。
在一开始就遇到了需要建立一个已知大小的2维vector的问题,回顾学习后发现写法是
vector< vector<int> > mat(m, vector<int> (n, 0));
其中0表示初始化其中所有值为0,m,n分别是2维数组的行数和列数。之后需要的是仔细,否则循环很容易出错。果不其然我就出错了,最后一个内部循环中错写成了
mat[j][cEnd] = num++; // 错误,应该是cStart,不是cEnd
改正后AC。
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> mat(n, vector<int>(n));
int len = n - 1;
int num = 1;
int rBegin = 0, rEnd = n - 1, cBegin = 0, cEnd = n - 1;
while (len > 0) {
for (int j = cBegin; j <= cEnd - 1; ++j) {
mat[rBegin][j] = num++;
}
for (int i = rBegin; i <= rEnd - 1; ++i) {
mat[i][cEnd] = num++;
}
for (int j = cEnd; j >= cBegin + 1; --j) {
mat[rEnd][j] = num++;
}
for (int i = rEnd; i >= rBegin + 1; --i) {
mat[i][cBegin] = num++;
}
len -= 2;
rBegin++, rEnd--;
cBegin++, cEnd--;
}
if (n % 2 != 0) {
mat[n / 2][n / 2] = num;
}
return mat;
}
};