【算法】双指针
双指针问题
双指针算法的核心逻辑
双指针算法主要用于解决数组划分也叫数组分块类的问题,通过两个指针(这里的指针也可以是数组的下标),将数组进行划分成三个部分,当cur指针进行扫描完整个区间后,即只剩下两个区间,即题目的要求。
283、移动零
题解
class Solution {
public:
void moveZeroes(vector<int>& nums)
{
int dest=0; //进行代表处理完毕的区间
int cur=0; //进行遍历过的区间
while(cur<nums.size())
{
if(nums[cur]!=0)
{
int temp=nums[dest];
nums[dest]=nums[cur];
nums[cur]=temp;
cur++;
dest++;
}
else
{
cur++;
}
}
}
};
1083、复写零
题解一:循环覆盖补零法
通过阅读题意
当数组中出现0的时候,需要将0进行复写,将0以后的元素进行向后移动一位,但是数组的长度是固定的,所以说最后一个元素就没了,这里我就直接进行想到了从0之后的位置进行循环向后移动进行覆盖,然后在进行复写0。在进行复写0后向后进行移动两位,防止进行全部置零处理了。
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int n = arr.size();
for (int i = 0; i < n; )
{
if (arr[i] == 0) {
// 将后面的元素全部右移一位
for (int j = n - 1; j > i; j--)
{
arr[j] = arr[j - 1];
}
if (i + 1 < n)
{
arr[i + 1] = 0; // 复制 0
}
i += 2; // 跳过已处理的 0
}
else
{
i++;
}
}
}
};
题解二:双指针算法
class Solution {
public:
void duplicateZeros(vector<int>& arr)
{
int cur=0;
int dest=-1;
while(1)
{
if(arr[cur]!=0)
{
++dest;
if(dest>=arr.size()-1)
{
break;
}
cur++;
}
else
{
dest+=2;
if(dest>=arr.size()-1)
{
break;
}
cur++;
}
}
cout<<cur<<dest<<endl;
while(cur>=0)
{
if(arr[cur]==0)
{
if(dest==arr.size())
{
dest--;
arr[dest]=arr[cur];
}
else
{
arr[dest]=arr[cur];
arr[--dest]=arr[cur];
}
cur--;
dest--;
}
else
{
//cout<<arr[cur]<<arr[dest]<<endl;
arr[dest]=arr[cur];
cur--;
dest--;
}
}
}
};
202、快乐数
题解
快慢指针法
class Solution {
public:
bool isHappy(int n)
{
int slow=n-1;
int fast=n;
int temp1=n;
int temp2=n;
while(fast!=slow)
{
int count=0;
while(temp1)
{
count+=(temp1%10)*(temp1%10);
temp1=temp1/10;
}
slow=count;
temp1=count;
int num=2;
while(num--)
{
count=0;
while(temp2)
{
count+=(temp2%10)*(temp2%10);
temp2=temp2/10;
}
temp2=count;
}
fast=count;
}
if(fast==1)
{
return true;
}
else
{
return false;
}
}
};
11、盛水最多的容器
题解
通过阅读题意,很容易进行想到通过暴力的方式进行解决,暴力的方式就是通过两个for循环进行依次取两条线进行进行暴力枚举,但是这种方式是超时的,不满足时间复杂度
影响水池中水的因素有两种,分别是两条线的距离和两条线中较短的那一条线,这里采取的思想是在刚开始的时候直接将两条线之间的距离设成最大,在两条线之间的距离变小的过程中,只有较短的那条线的长度变长时才有可能容量变大。
class Solution {
public:
int maxArea(vector<int>& height)
{
int left=0;
int right=height.size()-1; //将两条线的距离设成最大
int capacity=(height[left]<height[right]?height[left]:height[right])*(right-left); //进行初始化容量
int newcap=0;
while(right>left)
{
//判断两条线中较短的那条线进行变长时的容量变化
if(height[left]<height[right])
{
left++;
newcap=(height[left]<height[right]?height[left]:height[right])*(right-left);
}
else
{
right--;
newcap=(height[left]<height[right]?height[left]:height[right])*(right-left);
}
if(newcap>capacity)
{
capacity=newcap;
}
}
return capacity;
}
};
611、有效三角形个数
题解
这道题也不难想,第一时间最容易进行想到的一般都是暴力的方法,但是这道题直接进行暴力解决问题的话需要进行三个for循环进行解决问题,时间复杂度是O(n*n*n)直接就超时了。
可以通过一个for循环和双指针进行对暴力的方法进行优化,将时间复杂度进行优化到O(n*n)
这种优化都是基于单调性进行的优化两个指针一般是不同侧的。
class Solution {
public:
class Compare
{
public:
bool operator()(int a,int b)
{
return a<b;
}
};
int triangleNumber(vector<int>& nums)
{
int left=0;
int right=0;
int count=0;
//进行数组排序
sort(nums.begin(),nums.end());
for(int i=nums.size()-1;i>1;i--)
{
left=0;
right=i-1;;
while (left < right)
{
if (nums[left] + nums[right] > nums[i])
{
count += right - left;
--right;
}
else
{
++left;
}
}
}
return count;
}
};
179、查找总价格为目标值的两个商品
题解:
暴力:
通过两个for循环进行将数组中的任意两个数进行求和然后进行比对,时间复杂度O(n*n)。
双指针:
通过双指针对暴力的方式进行优化,先通过排序的方式进行将数组处理成有序,然后依然可以通过单调性进行优化。时间复杂度O(logN+O(n))
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target)
{
int left=0;
int right=price.size()-1;
//进行排序
sort(price.begin(),price.end());
while(left<right)
{
if(price[left]+price[right]>target)
{
right--;
}
else if(price[left]+price[right]<target)
{
left++;
}
else
{
break;
}
}
return {price[left],price[right]};
}
};
15、三数之和
题解
暴力
直接无脑三个for循环然后进行遍历。
双指针
通过双指针进行优化,这道题的思路上有效三角形的个数那道题的思想非常的相似。通过for循环进行固定一个数,然后通过双指针进行进行优化。
这道题最最最恶心的一个点是需要进行去重,这里着重说一下去重的思路,需要进行内部去重和外部去重,内部去重就是for循环进行固定的数进行去重(跳过相同的数),外部去重(双指针进行去重)。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
//进行排序
sort(nums.begin(),nums.end());
vector<vector<int>> ret={};
for(int i=nums.size()-1;i>1;i--)
{
int left=0;
int right=i-1;
//进行内部去重
if (i < nums.size()-1&& nums[i] == nums[i+1]) continue;
while(left<right)
{
if(nums[left]+nums[right]+nums[i]>0)
{
right--;
}
else if(nums[left]+nums[right]+nums[i]<0)
{
left++;
}
else
{
ret.push_back({nums[left],nums[right],nums[i]});
//进行外部去重
while(left < right &&nums[right]==nums[right-1])
{
right--;
}
while(left < right &&nums[left+1]==nums[left])
{
left++;
}
left++;
right--;
}
}
}
return ret;
}
};
18、四数之和
题解
双指针
和上一道题思路一样,不进行解释了。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
int n = nums.size();
if (n < 4) return res;
sort(nums.begin(), nums.end());
for (int i = 0; i < n-3; ++i) {
// 跳过重复值
if (i > 0 && nums[i] == nums[i-1]) continue;
// 提前终止条件(可选优化)
if ((long long)nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) break;
if ((long long)nums[i] + nums[n-3] + nums[n-2] + nums[n-1] < target) continue;
for (int j = i+1; j < n-2; ++j) {
// 跳过重复值
if (j > i+1 && nums[j] == nums[j-1]) continue;
int left = j+1, right = n-1;
while (left < right) {
long long sum = (long long)nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
res.push_back({nums[i], nums[j], nums[left], nums[right]});
// 跳过重复值
while (left < right && nums[left] == nums[left+1]) ++left;
while (left < right && nums[right] == nums[right-1]) --right;
++left;
--right;
}
else if (sum < target) ++left;
else --right;
}
}
}
return res;
}
};