977.有序数组的平方
随想录文字讲解:代码随想录
随想录视频讲解:双指针法经典题目 | LeetCode:977.有序数组的平方_哔哩哔哩_bilibili
状态:做出来了
题目:给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
第一次看这个题,发现要点在于寻找负数和非负数的界限,再以这个边界依次比较两边元素平方的大小,也就是相当于给定两个有序数组,将两个数组排序成一个数组,即归并排序。
找界限:最初想法是两个指针在头尾然后双指针向中间遍历,写时觉得麻烦发现可以用二分查找来确定这个边界
二分查找寻找正负边界代码:
int l=-1;
int r=nums.size();
int mid;
while(l+1!=r)
{
mid=(l+r)/2;
if(nums[mid]<0) l=mid;
else r=mid;
}
分好界限后就可以排序了,但是在此之前要判断一下边界条件,也就是l==-1和r==num.size()的情况。 前者是左指针没有动,也就是整个数组都是非负数,后者是右指针没动,也就是整个数组都是负数,检查界限的代码应为:
if(l==-1)
{
for(int i=0;i<nums.size();i++)
v.push_back(nums[i]*nums[i]);
return v;
}
else if(r==nums.size())
{
for(int i=nums.size()-1;i>=0;i--)
v.push_back(nums[i]*nums[i]);
return v;
}
但是其实根本不用写这段代码
归并排序中有某个数组提前结束的条件,对于其中一个为空数组也是适用的(毕竟一个数组结束就等于他变为了空数组)
整题代码为:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int l=-1;
int r=nums.size();
int mid;
vector<int> v;
while(l+1!=r)
{
mid=(l+r)/2;
if(nums[mid]<0) l=mid;
else r=mid;
}
while(l>=0&&r<nums.size())
{
if(nums[l]*nums[l]>nums[r]*nums[r])
{
v.push_back(nums[r]*nums[r]);
r++;
}
else
{
v.push_back(nums[l]*nums[l]);
l--;
}
}
if(l<0)
while(r<nums.size()) v.push_back(nums[r]*nums[r++]);
if(r==nums.size())
while(l>=0) v.push_back(nums[l]*nums[l--]);
return v;
}
};
学习随想录的算法:
我以为将双指针法改为二分是一种便捷的做法,但是我发现弄巧成拙了,我的上述做法先二分查找再归并排序实际上时间复杂度为O(n+logn),但是双指针做法只需要O(n)即只需要遍历一遍数组就够了。
但是在看过随想录的算法后,我复现时出现了相当多的错误。
随想录的做法:在原数组头尾搁放一个指针,创建一个新数组,然后将两指针中平方较大者放在新数组的末尾,从尾部依次向前放。
我出现的错误:1.将数组中较小值放在了末尾。2.将数字存在数组时使用vector容器中的是v.push_back,这是将新数字放在数组的末尾,与该做法的数组从后向前填出现矛盾。
因为leetcode上返回的是vector容器,因此,要将数字从后向前填应提前创建一个和原容器一样大的新容器,然后用指针从后向前填写。
209.长度最小的子数组
随想录文字讲解:代码随想录
随想录视频讲解:双指针法经典题目 | LeetCode:977.有序数组的平方_哔哩哔哩_bilibili
状态:做出来了
这道题之前做过,所以有一个滑动窗口的印象,说实话没什么自己的思考,第一次做的时候只会暴力,然后学习了滑动窗口而已。
滑动窗口代码:
int i=0,j=0;
int sum=0;
int length=nums.size()+1;
int l;
for(;j<nums.size();j++)
{
sum+=nums[j];
while(sum>=target)
{
l=j-i+1;
length=min(l,length);
sum-=nums[i];
i++;
}
}
令j指针向前走,算范围内的sum和,当sum和大于等于target的时候,因为是取长度最小的子数组,所以j不需要再往前走了,而是记录当前长度,再去处理i,当处理到范围内sum和小于target时,这时已经获取j在此处以及之前的最小长度,j++。
自己编写时出现的错误:
将while(sum>=target)写成了if(sum>=target),这样再范围内新加了一个特大数的时候,由于不能循环处理范围,将得不到最小的距离,而且当j走到num.size()时,数组将停止循环,会剩余i没有被处理。
59.螺旋矩阵II
随想录文字讲解:代码随想录
随想录视频讲解:一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili
状态:做出来了
这也是一道凭借着对着答案的记忆写出来的题,最初写的时候思维很混乱,看了视频讲解之后清晰多了,这题的要点主要在于理清循环的不变性和对变量的调整。将每个正方形的左顶点作为循环的起始点,即i=startx,j=starty,再对循环的终点进行调整即n-order,令order和startx,starty每次循环后++。由于为正方形,正方形框的数量为n/2,即循环条件的次数,循环结束后判断正方形长度为奇/偶,奇数便为余下的数赋值。
但是这样的写法在处理非正方形的矩阵上需要对矩阵的类型有精确判断,这是比较困难的(至少现想比较困难)
leetcode题解给出的矩阵的打印方法为在循环里先打印上面的边和右边的边,然后在用if判断来决定要不要打印下面和左边的边(用来打印长为1的一竖条和宽为1的一横条以及一个小方格)
具体代码为:
while (startx <= endx && starty <= endy) {
// 从左到右遍历上层
for (int j = starty; j <= endy; ++j) {
res.push_back(matrix[startx][j]);
}
// 从上到下遍历右侧
for (int i = startx + 1; i <= endx; ++i) {
res.push_back(matrix[i][endy]);
}
// 如果还有下层,则从右到左遍历下层
if (startx < endx) {
for (int j = endy - 1; j >= starty; --j) {
res.push_back(matrix[endx][j]);
}
}
// 如果还有左侧,则从下到上遍历左侧
if (starty < endy) {
for (int i = endx - 1; i > startx; --i) {
res.push_back(matrix[i][starty]);
}
}
// 向内层移动
++startx;
++starty;
--endx;
--endy;
}
循环用<=的条件来保证用整个循环来完整输出,在循环内部用<的if来解决end=start的特例(即长或宽为1)
今天写的时候边写边卖关子,对题的记忆比较新的缘故,写的时候的思考并不多,只是按照记忆搬运,但是感觉对思路更理解了,也是有收获