双指针法、滑动窗口法、螺旋矩阵
1.双指针法解有序数组的平方
1.1题目要求
LeetCode977有序数组的平方
题目内容:给你一个按非递减顺序排序的整数数组 nums,返回 每个数字的平方组成的新数组,要求也按非递减顺序排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
1.2题目思路
1.2.1暴力破解法
声明:暴力破解法放到Leetcode会超时,因此本题主要还是用双指针法,并给出双指针法的源代码,暴力破解法的思路如下:
1.首先用一个for循环,计算数组平方后的数字
2.利用快速排序,对数组进行重新排序,按照题目要求从小到大排序
1.2.2双指针法
1.2.2.1 为什么会想到滑动窗口法呢?
首先我们来观察一下这道题,题目本来就是按照由小到大的顺序排列的,在平方之后,会因为负数的增大而改变顺序,如果是这样的话,那么平方后最大的数字必然是在数组的两边,并且由两边到中间逐渐递减。
1.2.2.2 本题思路介绍
双指针法解本道题的主要思路如下:
1.首先分别设立两个指针i,j;
2.i指针从数组的前面开始遍历,而j指针从数组的后面开始遍历,计算并比较nums[i]×nums[i]和nums[j]×nums[j],取较大的放在result数组(返回结果的数组)的最后
3.所指元素已经放到result数组中的指针向中间移动,继续第2步操作,直到把所有的序列排好(i与j相遇)
源代码如下:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(),0);
int n=nums.size();
int t=n-1; //t指针用于更新result数组的下标
int i=0,j=n-1; //i指针从最前面开始遍历,j指针从最后面开始遍历
while(i<=j) //注意这里要带等号,因为所遍历的最后一个元素也要放到result数组中
{
if(nums[i]*nums[i]>nums[j]*nums[j])
{
result[t]=nums[i]*nums[i];
i++;
t--;
}
else
{
result[t]=nums[j]*nums[j];
j--;
t--;
}
}
return result;
}
};
2.滑动窗口法解长度最小的子数组
2.1题目要求
LeetCode209长度最小的子数组
题目内容:给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
2.2题目思路
2.2.1.暴力破解法
用两层for循环,从头开始遍历,第一层for循环(i)从第一个元素遍历到最后一个,第二个for循环从第一个for循环的元素(i)开始遍历,遍历到符合题意,即sum>=target为止,源代码如下:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
int min=10000;
for(int i=0;i<n;i++)
{
int sum=0;
int t=0;
//这里忘记加j<n导致溢出了
for(int j=i;sum<target&&j<n;j++)
{
sum += nums[j];
t++;
}
//这里不能直接写成t<min
if(sum>=target&&t<min)
min=t;
}
if(min==10000)
min=0;
return min;
}
};
2.2.2.1 易错点
如你所见,我把我写的过程中犯的错误标注到代码里了,下面我将一一解释我所犯的错误,以及得出的结果,希望你不要再犯。
错误一:第二层for循环忘记少加了一个j的限制条件,即j<n,导致数组越界。
这个错误在LeetCode中的表现是其下一行代码sum+=nums[j]报错,并且告诉你int数据类型越界,这个错误一时间不容易想到是for循环的错,正常人的思路是,先去查看target的取值范围,发现好像没有越界,我后来是看到了其显示的是两个负数相加,才知道是数组越界。
错误二:直接写成了t<min,就把t赋值给min,而没有考虑循环结束后是否满足我们所需要的条件。
2.2.2 滑动窗口法
在讲解滑动窗口法之前,我们首先来思考本题为什么可以用滑动窗口法:
观察题干,要求我们找到长度最小的连续子数组,而滑动窗口法的前一个指针正是往后滑动,去减少数组的长度,每次滑动都是等i指向了在该for循环的j下的最大值后,j才会向后滑动,只要j向后滑动,这里的长度就会变大,大于现在的min,所以不用考虑在这个j下i往前挪可能也有符合题意的子数组。(这一段有点讲不清楚啊啊啊,有没有大佬可以概括一下)
源代码如下:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n=nums.size();
//还是不能直接给min赋值为100000
int i=0,j=0,min=INT32_MAX;
int sum=0;
for(j=0;j<n;j++)
{
sum+=nums[j];
while(sum>=target)
{
if(min>(j-i+1))
min=j-i+1;
sum-=nums[i++];
}
}
//主要是这里的问题
if(min==INT32_MAX)
min=0;
return min;
}
};
2.2.2.1易错点
暴力破解法的时候,我直接给min赋值了100000,也就是最大的target,因为暴力破解法在LeetCode中也跑不出来,所以我也就没有意识到问题的错误,但是当用滑动窗口法可以跑出来之后,我发现有一个测试用例,min正好等于100000的时候符合题意,也就是正好取到它的最大值,然后我试图改进,把if(min= =100000)这个判断语句改成if(min==100000)&&target!=sum),结果发现还是无法通过那个测试样例,我返回去看了一眼代码,才发现当sum>=target,也就是说,当sum=target的情况下,进入while循环依旧会改变sum的值(这里写成sum>=target是因为题目说的是大于等于)。
所以这里还是写成min= = INT32_MAX,因为加最后一个if判断语句的目的就是看看是否有整个数组都加进去但是仍然小于target的情况。
3.螺旋矩阵II
3.1题目要求
LeetCode59螺旋矩阵II
题目内容:给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
3.2 解题思路
本题没有什么算法技巧,纯逻辑题,但是刚做这道题时容易处理不好边界问题,卡哥的建议是,四个方向的便利都选择相同的区间,即左闭右开,也就是说,把最后一个放到下一个for循环中处理,下面简要介绍一下本题定义的参数:
loop:要循环的次数
target:循环了第几圈,主要是用来计算这次循环的矩形长度(n-target)
startx:循环开始的行数
starty:循环开始的列数
值得注意的是:当n为奇数时,循环完之后还要在最中间的小方格加上最大的数字
源代码如下:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> a(n, vector<int>(n, 0));
int startx=0,starty=0,target=1;
int num=1;
int loop=n/2;
while(loop--)
{
int j=starty;
//横向循环上面一行,本行最后一个(也就是右上角)不在这层循环里
for(;j<n-target;j++)
{
a[startx][j]=num++;
}
//纵向循环最后一列,右上角在这层循环里,但是这一列的最后一个(右下角)不在这层循环里
int i=startx;
for(;i<n-target;i++)
{
a[i][j]=num++;
}
//横向循环最下面一行,右下角在这个循环里,左下角不在
for(;j>starty;j--)
{
a[i][j]=num++;
}
//纵向循环最前面一列,左下角在这个循环里,但右上角不在
for(;i>startx;i--)
{
a[i][j]=num++;
}
target++;
startx++;
starty++;
}
if(n%2==1)
a[n/2][n/2]=num++;
return a;
}
};
总结反思
1.在找子数组的最大或最小长度时,可以考虑滑动窗口法
2.要为某个变量设最大值时,最好不要瞎赋值,而是写成INT32_MAX这种
3.像第三个题目这种,最好四个循环保持区间端点的一致性,不然会很难调这个边界值,奇数和偶数的不同不一定非要分开写两个程序,有时候在后面加个判断语句就可以了
4.注意一定不要让数组越界
5.当要赋值min这种数值时,除了要判断其大小,还要判断是否满足条件
写给自己的话
不要过分对未来焦虑,不要过分考虑未来,不要考虑失败后怎么样,先把自己手头的事情做好,一点一点的来,进步是需要慢慢积累的,加油呀!
以及,我好喜欢学习算法时思考的感觉和做出一道题的成就感!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构