双向指针问题01
1.167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)
题解:1.暴力解法直接两层循环,复杂度为0(n^2)
2.利用双向指针(当a[0]+a[n-1]>target,说明a[1]+a[n-1]>target是必然事件)
cpp
class Solution {//利用代码有序这一特点,利用两个相向的指针,将复杂度0(n^2)化为0(n)
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int n=numbers.size();
int i=0;int j=n-1;
while(i!=j){
if(numbers[i]+numbers[j]>target)
j--;
else if (numbers[i]+numbers[j]<target)i++;
else return {i+1,j+1};
}
return {-1,-1};
}
};
思路:三数之和等于target ,可以将其化为两数之和的问题(a+b+c=target==》a+b=target-c)
代码:
cpp
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
// i != j、i != k 且 j != k
//整数数组 nums 无序--》有序
//答案中不可以包含重复的三元组
int n=nums.size();
int i,j,k;//把i固定,用j,k来双向遍历
//nums.sort();
ranges::sort(nums);
vector<vector<int>> ans;
for(int i=0;i<n-2;i++){//i=n-3;j=n-2;k=n-1
if(i&&nums[i]==nums[i-1])
continue;//跳过重复元素
int j = i + 1, k = n - 1;
while(j<k){//这里因为后续回跳过重复元素,所以不能用不等号
int sum=nums[i]+nums[j]+nums[k];
if(sum>0) k--;
else if(sum<0)j++;
else {
ans.push_back({nums[i],nums[j],nums[k]});
for (j++; j < k && nums[j] == nums[j - 1]; j++); // 跳过重复数字
for (k--; k > j && nums[k] == nums[k + 1]; k--); // 跳过重复数字
}
}
//if(nums[i]==nums[i+1])
// continue;//跳过重复元素
}
return ans;
}
};
课后练习:
给你一个下标从 0 开始长度为 n
的整数数组 nums
和一个整数 target
,请你返回满足 0 <= i < j < n
且 nums[i] + nums[j] < target
的下标对 (i, j)
的数目。
cpp
class Solution {
public:
int countPairs(vector<int>& nums, int target) {
//开始长度为 n 的整数数组 nums,数组无序
int ans=0;
int n=nums.size();
sort(nums.begin(),nums.end());
int i=0,j=n-1;
while(i<j){
if(nums[i] + nums[j] < target){
ans+=(j-i);//ans要加上j到i之内所有数字。
i++;
}
else j--;
}
return ans;
}
};
ans
的增量应该是计算满足条件的所有下标对,而不仅仅是每次找到一个满足nums[i] + nums[j] < target
的时候增加 1。实际上,当前
i
和j
满足条件时,你需要计数i
到j-1
之间的所有对。可以通过累加j-i
来解决这个问题。
cpp
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
//三个数,转化为三数之和问题,最接近使用一个min_disum;
sort(nums.begin(),nums.end());
int n=nums.size();
int i,j,k;
int min_disum=INT_MAX;//用来定义和与target的差
int sum=0;
int ans;
for(i=0;i<n-2;i++){
j=i+1;k=n-1;
while(j<k){
sum=nums[i]+nums[j]+nums[k];
min_disum=min(min_disum,abs(sum-target));
if(min_disum==abs(sum-target)){
ans=sum;
}
if(min_disum==0)
return sum;
else if(sum>target){
k--;
}
else {
j++;
}
}
}
return ans;
}
};
cpp
class Solution {
public:
vector<vector<int>> fourSum(vector<int> &nums, int target) {
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
int n = nums.size();
for (int a = 0; a < n - 3; a++) { // 枚举第一个数
long long x = nums[a]; // 使用 long long 避免溢出
if (a > 0 && x == nums[a - 1]) continue; // 跳过重复数字
if (x + nums[a + 1] + nums[a + 2] + nums[a + 3] > target) break; // 优化一
if (x + nums[n - 3] + nums[n - 2] + nums[n - 1] < target) continue; // 优化二
for (int b = a + 1; b < n - 2; b++) { // 枚举第二个数
long long y = nums[b];
if (b > a + 1 && y == nums[b - 1]) continue; // 跳过重复数字
if (x + y + nums[b + 1] + nums[b + 2] > target) break; // 优化一
if (x + y + nums[n - 2] + nums[n - 1] < target) continue; // 优化二
int c = b + 1, d = n - 1;
while (c < d) { // 双指针枚举第三个数和第四个数
long long s = x + y + nums[c] + nums[d]; // 四数之和
if (s > target) d--;
else if (s < target) c++;
else { // s == target
ans.push_back({(int) x, (int) y, nums[c], nums[d]});
for (c++; c < d && nums[c] == nums[c - 1]; c++); // 跳过重复数字
for (d--; d > c && nums[d] == nums[d + 1]; d--); // 跳过重复数字
}
}
}
}
return ans;
}
};
对于 Java、C++ 等语言,注意相加结果可能会超过 32 位整数范围,需要用 64 位整数存储四数之和。
cpp
class Solution {
public:
int triangleNumber(vector<int>& nums) {
//有效三角形,a+b>c;a-b<c;
sort(nums.begin(),nums.end());
int n=nums.size();
int i,j,k;
int ans=0;
//如果a+b>c;--->a+b+n>c;---->如果a,b>c,则会有b-a个
for(k=n-1;k>1;k--){
i=0;j=k-1;
while(i<j){
int sum=nums[i]+nums[j]-nums[k];
if(sum>0){
ans=ans+j-i;
j--;//sum应该变小
}
else //sum<0;数应该变大;
i++;
}
}
return ans;
}
};