基础知识:
双指针简介:
双指针(Two Pointers):指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。如果两个指针方向相反,则称为「对撞指针」。如果两个指针方向相同,则称为「快慢指针」。如果两个指针分别属于不同的数组 / 链表,则称为「分离双指针」。
在数组的区间问题上,暴力算法的时间复杂度往往是。而双指针利用了区间「单调性」的性质,可以将时间复杂度降到 。
对撞指针适用范围+部分例题
对撞指针一般用来解决有序数组或者字符串问题:
- 查找有序数组中满足某些约束条件的一组元素问题:比如二分查找、数字之和等问题。
- 字符串反转问题:反转字符串、回文数、颠倒二进制等问题。
例题:
这道题理解了 对撞指针就很好想出一个思路--两头直接开始比较(另一个也可以用栈)。但本体还有一个比较讨厌的地方---字符串格式转换
class Solution {
public:
bool isPalindrome(string s) {
if(s.size()<=1) return true;
string sive;
for(char &c:s){
if(isalnum(c)) sive+=tolower(c);
}
int left=0;int right=sive.size()-1;
while(left<right){
if(sive[left]==sive[right]){
left++;right--;
}
else
return false;
}
return true;
}
};
字符串部分代码解释:
isalnum(c)//c是否为数字或者字母。
isdigit()//数字?
isalpha()//字母?
tolower(c);//c无论大小写同意变换为小写
s.size();//string 求长度用
思路:本题要求找到底乘高的面积最大值。其中,高是两个杆子之中最短的那个,底是两个杆子位置差。假设选中i,j两个杆子:高h=min(hight[i],hight[j]); 底d=j-i。
那么我们常规方法得到i,j 需要两层for循环遍历,但可根据双指针方法,减少遍历次数。
核心思想:
在每个状态下,无论长板或短板向中间收窄一格,都会导致水槽 底边宽度 −1 变短:
若向内 移动短板 ,水槽的短板 min(h[i],h[j]) 可能变大,因此下个水槽的面积 可能增大 。
若向内 移动长板 ,水槽的短板 min(h[i],h[j])不变或变小,因此下个水槽的面积 一定变小 。
因此,初始化双指针分列水槽左右两端,循环每轮将短板向内移动一格,
并更新面积最大值,直到两指针相遇时跳出;即可获得最大面积。
可以参考题解:11. 盛最多水的容器 - 力扣(LeetCode)
代码实现:
class Solution {
public:
int maxArea(vector<int>& height) {
int i=0;int j=height.size()-1;
int ans=0;
while(i<j){
int h=min(height[i],height[j]);int d=j-i;
ans=max(ans,h*d);
if(h==height[i]) i++;
else j--;
}
return ans;
}
};
矩阵查找值,优先考虑双指针+缩减区域。
大体思路:本题需要搜索全部区域(为什么从右上角?左下角出发-----可以参考链接),所以跳出循环的条件是到头(行 :0—m-1,列:n-1—0)
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m=matrix.size();int n=matrix[0].size();
int row=0;int col=n-1;int num;
while(row<=m-1&&col>=0){
num=matrix[row][col];
if(num>target) col--;
else if(num<target) row++;
else return true;
}
return false;
}
};
881. 救生艇 :(和上一篇文章 力扣基础刷题---二分查找-CSDN博客里面的167题思路一样 )
class Solution {//每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit
public:
int numRescueBoats(vector<int>& people, int limit) {
sort(people.begin(),people.end());
int ans=0;//存组合数
int i=0;int j=people.size()-1;
while(i<j){
int start=people[i];int end=people[j];
if(start+end<=limit){
ans++;i++;j--;//数据只能用一次
}
else if(start+end>limit){
ans++;j--;
}
}
if(i==j) ans++;
return ans;
}
};
从 nums 中选三个数,满足 1≤a≤b≤c 且 a+b>c 的方案数。
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());//为了能够使用相向双指针,先对数组从小到大排序。
int ans=0;//存组合数
for(int c=2;c<nums.size();c++){//外层循环枚举 =nums[c]
int a=0;int b=c-1;
while(a<b){
if(nums[a]+nums[b]>nums[c]){
ans+=b-a;//若果a+b>c 那么a+n+b>c必然成立.这其中一共有b-a个数字
b--;//和大了就减小
}
else a++;//和小了就增大
}
}
return ans;
}
};
读题目发现和上面的题目十分相似,于是写出主要部分。
class Solution { //和三角形题目很像
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
if (nums.size() < 3)
return ans;
sort(nums.begin(), nums.end());
for (int k = 2; k < nums.size() ; k++) {
int i = 0, j =k-1;
if (nums[i] > 0)
break;
// 如果当前数字大于0,则三数之和一定大于0,所以结束循环--->直接到return
//或者可以将break换位return ans;
while (i < j) {
cout<<nums[i]<<nums[j]<<nums[k]<<endl;
if (nums[i] + nums[j] + nums[k] == 0) {
ans.push_back({ nums[i], nums[j],nums[k]});
i++;
j--;
} //为保持平衡需要一变大,另一变小
else if (nums[i] + nums[j] + nums[k] > 0) {
j--;
} //和大了,减少
else {
i++;
} //和小了,增大
}
}
return ans;
}
};
但是本题需要去重,太难了。
去重 核心代码://if前面的条件判断也很重要--->防止越界。
i++;
if(i>0&&nums[i]==nums[i-1]) i++;
//i++之后,和上一个相比。不能和下一个相比,否则会漏掉这个(一下子i+2)
j--;
if(j<n&&nums[j]==nums[j+1])j--;
class Solution {//和三角形题目很像
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
if(nums.size()<3) return ans;
sort(nums.begin(),nums.end());
for(int k=0;k<nums.size()-2;k++){
int i=k+1,j=nums.size()-1;
if(nums[k] > 0) break;
// 如果当前数字大于0,则三数之和一定大于0,所以结束循环--->直接到return
if(k>0&&nums[k]==nums[k-1]) continue;//---》到for循环;
while(i<j){
if(nums[i] + nums[j] + nums[k] == 0){
ans.push_back({nums[k],nums[i],nums[j]});
i++;j--;
while (i<j && nums[i] == nums[i-1]) i++; // 去重
while (i<j && nums[j] == nums[j+1]) j--; // 去重
}
else if(nums[i] + nums[j] + nums[k] > 0){
j--;
while (i<j && nums[j] == nums[j+1]) j--;
}//和大了,减少
else{i++;
while (i<j && nums[i] == nums[i-1]) i++;
} //和小了,增大
}
}
return ans;
}
};