学习了代码随想录第4节、5节、6节内容。
第4节是977有序数组的平方,方法包括暴力解法(先平方后排序)、官方给出的双指针(找到负数和非负数的分界线,利用类似归并排序和双指针,将平方后已经有序的子数组排序)、卡哥给出的双指针(与官方给出的双指针不同,是从两边开始)
题目的几个条件和要求:
①非递减排序的整数组nums(也就是说有可能存在相等)
②返回每个元素平方后组成的新数组,且按照非递减顺序排序
代码实现:手撕不来,一边查资料理解,一边默写下来(后面我尽量能够不参考资料手写下来)
学习了代码随想录第4节、5节、6节内容。
第4节是977有序数组的平方sortedSquares,方法包括暴力解法(先平方后排序)、官方给出的双指针(找到负数和非负数的分界线,利用类似归并排序和双指针,将平方后已经有序的子数组排序)、卡哥给出的双指针(与官方给出的双指针不同,是从两边开始)
题目的几个条件和要求:
①非递减排序的整数组nums(也就是说有可能存在相等,至少含1个元素吧)
②返回每个元素平方后组成的新数组,且按照非递减顺序排序
代码实现:手撕不来,一边查资料理解,一边默写下来(后面我尽量能够不参考资料手写下来)
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
for ( int i = 0; i < nums.size(); i++) {
// 实际上你可以完整地写下来,让思维流动,遇到阻塞耐心解决
nums[i] *= nums[i];
}
sort(nums.begin(), nums.end());
return nums;
}
};
这里的sort应该属于快速排序!
官方给出的双指针解法,有点抽象,从头一点一点分析来看。
因为nums.size()后面多次使用到,所以令n=nums.size()。negativa是最大负数下标,肯定是非负的,先令成-1,方便后面判断是否找到&存在。
然后就是一层for循环,嵌入if语句找到negative值,使用了break跳出循环的技巧。
紧接着是双指针,先定义了数组ans,分界线设置两个指针i和j,使用while循环,嵌套if语句。具体来看,while的循环条件i>=0 || j<n,补集为i<0 && j>=n,要知道最大索引下标为n-1,但为什么要用或?如果举一个[2]和[-2]的单元素数组例子,就明白了,当[2]时,i=-1,j=0符合(所以终于明白为什么要赋值-1了,赋给其他值是不行的),同理当[-2]时,i=0,j=1符合。这种条件考虑到了单元素的特例。同时0和n都是作为i和j移动的边界条件。
再来看if条件,4个条件中,第一个if条件对应全为正数(此时i=-1),第二个if条件对应全为负数(此时i=n-1,j则等于n且不会大于n),后面两个if条件则对应i,j均在数组范围内(即有负数,也有非负数的情况),取比较后的最小值。理解每一次的while循环,实际都是在取一个最小值放入ans中,因为在取的过程中,i和j是动态变化的(数组可能取着取着就变成了全正数或全负数,这个时候应该按照什么规则取值呢?所以给出了第一个第二个条件)。
对代码的把控,应该来自于对其深入细致反复的理解。
根据以上剖析,可以很轻松地写出代码:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
int negative = -1;
for (int i = 0; i < n; i++) {
if (nums[i] < 0) {
negative = i;
} else {
break;
}
}
vector<int> ans;
int i = negative;
int j = negative + 1;
while (i >= 0 || j < n) {
if (i < 0) {
ans.push_back(nums[j] * nums[j]);
j++;
} else if (j == n) {
ans.push_back(nums[i] * nums[i]);
i--;
} else if (nums[i] * nums[i] < nums[j] * nums[j]) {
ans.push_back(nums[i] * nums[i]);
i--;
} else {
ans.push_back(nums[j] * nums[j]);
j++;
}
}
return ans;
}
};
卡哥给出的双指针法,明显就简单很多,也可以分析一下。与官方的方法不同点在于是从两端从最大值找起,对result新数组是从末尾开始填充(因此要先定下尺寸-和nums一样大小)。首先定义result,再定义k=nums.size()-1,因为后面要用k来指向result,并不断递减填充。但是每次填充i或j要么加要么减,只存在一种情况,所以后面跟着for循环语句同时初始化i和j,并在嵌入的if语句中分别更新i和j而不在for条件内更新。要更新的是i和j(分别在两个条件中更新)以及k(每个条件都更新)。不难写出代码:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(), 0);
int k = result.size() - 1;
for (int i = 0, j = nums.size() - 1; i <= j;) {
if (nums[i] * nums[i] < nums[j] * nums[j]) {
result[k--] = nums[j] * nums[j];
j--;
} else {
result[k--] = nums[i] * nums[i];
i++;
}
}
return result;
}
};
第5节讲了209长度最小的子数组minSubArrayLen,给了含n个正整数的数组和正整数s,找出长度最小的连续子数组和大于等于s,返回其长度否则返回零。有暴力解法和滑动窗口两种解法,滑动窗口来源于对暴力解的简化。
class Solution209_0 {
public:
int minSubArrayLen(vector<int>& nums, int s) {
int result = INT32_MAX;
int sum = 0;
int subLength = 0;
for (int i = 0; i < nums.size(); i++) {
sum = 0;
for (int j = i; j < nums.size(); j++) {
if (sum >= s) {
subLength = (j - i + 1);
result = result < subLength ? result : subLength;
break;
}
}
}
return result == INT32_MAX ? 0 : result;
}
};
滑动窗口解法:不断调整子数组的起始和终止位置
class Solution209_1 {
public:
int minSubArrayLen(int s, vector<int>& nums) {
int result = INT32_MAX;
int sum = 0;
int i = 0;
int subLength = 0;
for (int j = 0; j < nums.size(); j++) {
sum += nums[j];
while (sum >= s) {
subLength = (j - i + 1);
result = result < subLength ? result : subLength;
sum -= nums[i++];
}
}
return result == INT32_MAX ? 0 : result;
}
};
第6节是59螺旋矩阵II,依据分析可以给出代码,因为关注点在代码技巧而不是算法的巧妙,所以不做详细解释了:
class Solution59_0 {
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--) {
i = startx;
j = starty;
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; // n * n
}
return res;
}
};
第5节和第6节还有对应的4道扩展题,其中水果成篮问题有一点头疼,后面再写专门文章解释分析。以上就是代码随想录Day2的内容了。