
🌈个人主页 🤞
✨个人专栏 🤗
🍀代码信条
每一行代码都是成长的脚印👣,每一次调试成功都是对坚持的回应
目录
双指针算法2
5.双指针_有效三角形个数

思路
解法一:利用三角形任意两边之和大于第三边的特性使用暴力枚举 来解这道题,但这个解法时间复杂度肯定非常之高,所以就不考虑了。
解法二:利用单调性,使用双指针算法来解决问题。
【1】首先需要对数组来排个序
【2】固定最大的数,而后将左边的数作为一个区间利用双指针来统计符合三元组要求的个数。
【3】如图,先判断L+R是否大于固定的M:
如果 (L+R)>M,则说明R加区间任何一个数都大于M,这时就可以直接统计三元组个数,然后将R向左移动一下;
如果(L+R)<=M,则说明L加区间任何一个数肯定都小于M,这时只需要将L向右移动一下 。
【4】直到L>=R,此时只需要将固定的数减减,又会有一个新的区间,继续重复上述步骤 就可以了。
代码实现
cpp
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
//固定最大数,利用双指针算法快速统计符合要求的三元组个数
int numb=0;
for(int M=nums.size()-1;M>0;M=M-1)
{
int L=0,R=M-1;
while(L<R)
{
if(nums[L]+nums[R]>nums[M])
{
numb+=(R-L);
R--;
}
else L++;
}
}
return numb;
}
};
6.双指针_和为s的两个数

思路
解法一:暴力枚举,从2开始向后枚举并和target比较,时间复杂度O(n^2),所以不考虑。
解法二:利用单调性,使用双指针算法解决问题
【1】定义两个指针left在最左边,right在最右边
【2】将两指针所在位置数组元素之和与target比较
【3】sum>target ,则说明right和right左边任意一数相加都大于target,所以right- -
sum<target ,则说明,left和left右边任意一数相加都小于target,所以left++
sum=target,返回结果
代码实现
cpp
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target) {
int left=0,right=price.size()-1;
while(left<right)
{
int sum=price[right]+price[left];
if(sum>target)
{
right--;
}
else if(sum<target)
{
left++;
}
else return {price[left], price[right]};
}
return {-1,-1};
}
};
7.双指针_三数之和

思路
解法一:首先看到这个题,还是会先想到暴力解法:排序+暴力枚举+set去重,时间复杂度O(N^3),由于复杂度太高,所以不考虑。
解法二:排序+双指针
【1】首先对这个数组进行排序
【2】其次从左往右固定 一个数M,在这个数的后面区间内使用双指针算法,快速找到两个和为-M的数 (这里可以参照上一题的思路)
【3】优化 ,我们在从到到右固定M这个数时,如果M>0时,区间右边的数肯定也都是大于0的,所以不可能找到两正数相加等于负数的情况,因而可以直接跳出循环
【4】但是在处理过程中需要注意到两个细节问题
细节一:不能漏掉任何一组数 :
在采用双指针算法找target时,若遇到了符合要求的一组数,不能像上一题一样直接返回这组数,而是将其存起来,然后left++,right- -继续寻找下一组数,直到left>=right
细节二:去重 :
【去重1 】,题目中要求不可包含重复的三元组,所以在找到题目要求的一组数进行left++和right- -之后还需要判断是否重复,重复则继续left++或者right--跳过重复元素。如图:
【去重2 】不仅M后面区间的数要去重,M本身在加加的过程中也需要去重。如图:
细节三:防止越界访问 :
在处理上述去重问题时,要防止left++直至越界 的问题;还要防止M在去重 过程中,一直++导致越界 的问题。
依据上述思路,下面来写代码:
代码实现
cpp
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//【1】首先对数组进行排序
std::sort(nums.begin(), nums.end());
//利用ans来存储结果
vector<vector<int>> ans;
//利用for循环,固定M
for(int M=0;M<nums.size()-1;)
{
//【3】优化:
if(nums[M]>0) break;
int target=-nums[M];
int left=M+1,right=nums.size()-1;
//【2】双指针算法
while(left<right)
{
int sum=nums[left]+nums[right];
if(sum>target) right--;
else if(sum<target) left++;
else{
ans.push_back({nums[M],nums[left],nums[right]});
//细节一:不能漏掉任何一组数
left++,right--;
//细节三:防止越界访问 && 细节二:M后的区间left,right去重问题解决
while(left<right&&nums[left]==nums[left-1]) left++;
while(left<right&&nums[right]==nums[right+1]) right--;
}
}
M++;
//细节三:防止M出现越界访问 && 细节二:M++后的去重问题解决
while(M<nums.size()-1&&nums[M]==nums[M-1]) M++;
}
return ans;
}
};
8.双指针_四数之和

思路
解法一:排序+暴力枚举+set去重,由于时间复杂度过大超出时间限制,因而不考虑
解法二:和上一题三数之和题解思路相同,排序+双指针
【1】排序
【2】从左往右依次先固定 一个数a。
然后将后面的数按照求三个数之和的思路:固定一个数b,将后面区间的数运用双指针算法,找出目标值。如图:
当a后面的数找完一轮目标值时,a继续++,而后继续重复上述步骤。
【3】同时不要忘记同样的细节问题:不漏掉任何一组数 ,a,b,L,R去重 ,防止越界问题
代码实现
cpp
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ans;
//排序
std::sort(nums.begin(),nums.end());
//用for循环嵌套
for(int a=0;a<nums.size()-1;)
{
int n=nums.size()-1;
for(int b=a+1;b<nums.size()-1;)
{
int target1=target-nums[a]-nums[b];
int L=b+1,R=n;
while(L<R)
{
//双指针算法,找目标值
int sum=nums[L]+nums[R];
if(sum<target1) L++;
else if(sum>target1) R--;
else{
ans.push_back({nums[a],nums[b],nums[L],nums[R]});
//不漏掉任何一组数,继续L++,R--
L++,R--;
while(L<R&&nums[L]==nums[L-1]) L++;//L去重
while(L<R&&nums[R]==nums[R+1]) R--;//R去重
}
}
b++;
while(b<n&&nums[b]==nums[b-1]) b++;//b去重
}
a++;
while(a<n&&nums[a]==nums[a-1]) a++;//a去重
}
return ans;
}
};
此时我们提交代码之后会发现出现溢出的错误,这是由于target1过大导致int无法装下,此时我们就可以换上long long类型

将int target1=target-nums[a]-nums[b];这句换成 long long target1=(long long)target-nums[a]-nums[b];这时就可以提交通过啦!这时候时间复杂度为O(N^3)
🎊到这儿,优选算法------【双指针算法2】C++实现的内容就结束了。如果文中有表述不准的地方,或是你有更清晰的理解思路,强烈欢迎在评论区留言交流------ 技术路上多碰撞,才能更快进步
觉得内容对你有帮助的话,别忘了点赞❤️➕收藏🌟,方便后续回顾复习;想跟着一起系统学习数据结构的朋友,也可以点击关注,下一期我们会聚焦更进一步的学习。不见不散✌️









