✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh--CSDN博客
✨ 文章所属专栏:c++篇--CSDN博客
文章目录
一.双指针算法
双指针算法
(Two Pointers Algorithm)
,又称快慢指针算法,龟兔赛跑算法等,是一种在链表,数组,矩阵等数据结构中,通过设置两个指针,并通过特定的移动策略来遍历数据元素同时解决问题的编程技巧。
双指针的分类:
- 对撞指针:两个指针方向相反移动
- 快慢指针:两个指针方向相同,但移动速度不同,通常快指针每次移动的距离大于慢指针
- 分离双指针:两个指针分别属于不同的数组或者链表
双指针算法的优势:
双指针算法最核心的用途是优化时间复杂度。原本两个指针有n2中组合,时间复杂度是O(n2)。而双指针算法通过运用单调性,使得指针只能单项移动,因此总的时间复杂度只有O(n)。
下面通过几个例题来讲解双指针的使用技巧
二.例题
1.移动零
题目:
算法原理:
代码实现:
cpp
void moveZeroes(vector<int>& nums) {
//双指针算法
int cur=0;
int dest=-1;
while(cur<nums.size()){
//cur指针遇到0,cur向前移动
if(nums[cur]==0){
cur++;
}
//cur指针遇到非0,交换cur指针和dest+1指针指向的值,交换完后两个指针再向前移动
else{
swap(nums[cur],nums[dest+1]);
dest++;
cur++;
}
}
}
2.复写零
算法原理:
代码实现:
cpp
void duplicateZeros(vector<int> &arr)
{
int cur = 0;
int dest = -1;
// 先找到复写后的最后一个元素位置
while (dest < (int)arr.size() - 1)
{
if (arr[cur])
{
dest += 1;
}
else
{
dest += 2;
}
if (dest < arr.size() - 1)
{
cur++;
}
}
// 处理边界情况
if (dest == arr.size())
{
arr[arr.size() - 1] = 0;
cur--;
dest -= 2;
}
while (cur >= 0)
{
// cur指针遇到非0元素,复写一次,两个指针都往前移动一次
if (arr[cur])
{
arr[dest--] = arr[cur--];
}
// cur指针遇到0元素,复写两次,dest指针先移动一次,两个指针再一起移动一次
else
{
arr[dest--] = 0;
arr[dest--] = 0;
cur--;
}
}
}
3.快乐数
算法原理:
代码实现:
cpp
int number(int n)
{
int sum = 0;
while (n)
{
int m = n % 10;
n /= 10;
sum += m * m;
}
return sum;
}
bool isHappy(int n)
{
int slow = n;
int fast = number(n);
while (slow != fast)
{
slow = number(slow);
fast = number(number(fast));
}
return slow == 1;
}
4.盛水最多的容器
算法原理:
代码实现:
cpp
int maxArea(vector<int> &height)
{
// 定义两个左右指针
int left = 0;
int right = height.size() - 1;
// 获取小的高度
int min = height[left] < height[right] ? height[left] : height[right];
// 求出当前容量假设为最大值
int vmax = min * (right - left);
// 左右两个指针向内移动,容器的宽度减少,想要获取更大的容量,只能找到大的高度
// 也就是舍弃小的高度,移动找大的高度
while (left < right)
{
if (height[left] < height[right])
{
left++;
}
else
{
right--;
}
min = height[left] < height[right] ? height[left] : height[right];
int v = min * (right - left);
if (v > vmax)
{
vmax = v;
}
}
return vmax;
}
5.有效三角形的个数
算法原理:
代码实现:
cpp
int triangleNumber(vector<int>& nums) {
//先将数组进行排序,可以减少三角形的判断情况
sort(nums.begin(),nums.end());
//设置最大值的指针max,在max指针左区间设置两个做右指针有来查找符合的组合
int max=nums.size()-1;
int left=0;
int right=max-1;
//设置变量size用来记录可以组合的个数
int size=0;
while(max>0){
//如果左指针值加上右指针值大于max指针最大值,[left,right-1]区间都符合,求出个数
//右指针减一
if(left<right&&nums[left]+nums[right]>nums[max]){
size+=(right-left);
right--;
}
//如果左指针值加上右指针值小于等于max指针最大值,left指针加一
else{
left++;
}
//当左右指针相遇时,从新设置最大值,在[left,max-1]区间继续查找
if(left>=right){
max--;
left=0;
right=max-1;
}
}
return size;
}
6.和为s的两个数
题目:
算法原理:
代码实现:
cpp
vector<int> twosum(vector<int>& nums,int target){
int left = 0;
int right = nums.size() - 1;
vector<int> v;
while(left<right){
if(nums[left]+nums[right]<target){
left++;
}
else if(nums[left]+nums[right]>target){
right--;
}
else{
v.push_back(nums[left]);
v.push_back(nums[right]);
break;
}
}
return v;
}
7.三数之和
算法原理:
因为题目要求返回的是数组元素,不是下标,所以可以先对数组进行排序,使其递增排列,这样就能使用两数之和的思想。除此之外还有一个注意点就是,每次找到符合要求的组合后,在移动指针时要判断是否会出现重复元素,如果出现重复元素要去重,减少不必要的遍历,可以降低时间复杂度。
代码实现:
cpp
vector<vector<int>> threesum(vector<int>& nums){
//先将数组进行排序
sort(nums.begin(), nums.end());
//定义一个二维数组用来存放组合数组
vector<vector<int>> vv;
//定义三个指针
int i = 0;
int left = i + 1;
int right = nums.size() - 1;
int target = 0 - nums[i];
//i指针指向的是小于等于零的元素,这样可以保证三数之和为0
while(nums[i]<=0&&left<right){
if(nums[left]+nums[right]>target){
right--;
}
else if(nums[left]+nums[right]<target){
left++;
}
else{
vv.push_back({nums[i],nums[left++],nums[right--]});
//左右指针遇到相等元素时,跳过去重
//处理边界情况,左指针要保证在右指针的左边,右指针要保证在左指针的右边
while(left<right&&nums[left]==nums[left-1]){
left++;
}
while(right>left&&nums[right]==nums[right+1]){
right--;
}
}
//当出现左右指针相等或者互换方向时,重新设置i指针的值,同时i指针也要去重
//处理边界情况,i可能会越界
if(left>=right){
i++;
while(i+1<nums.size()&&nums[i-1]==nums[i]){
i++;
}
left=i+1;
right = nums.size() - 1;
target = 0 - nums[i];
}
}
return vv;
}
8.四数之和
算法原理:
四数之和这里就不再详细地讲解算法原理,其实就是三数之和的改版,在三数之和的基础上再增加一个指针
i指针固定第一个数,j指针固定第二个数,两数之和值=target-nums[i]=nums[j]
在三数之和的基础上增加一个循环,同时也要注意去重
如果前面两数之和,三数之和明白算法原理后,这里四数之和就会比较轻松。所以一定要先明白前面两个,再来尝试解这道题
代码实现:
cpp
vector<vector<int>> foursum(vector<int>& nums,int target){
//先排序
sort(nums.begin(),nums.end());
vector<vector<int>> vv;
int left, right;
//i指针固定第一个值
for(int i=0;i<nums.size();){
//j指针用来固定第二个值
for (int j = i + 1; j < nums.size();){
long long targetj = (long long)target - nums[i] - nums[j];
left = j + 1;
right = nums.size() - 1;
while(left<right){
if(nums[left]+nums[right]>targetj){
right--;
}
else if(nums[left]+nums[right]<targetj){
left++;
}
else{
vv.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--;
}
}
}
j++;
while(j<nums.size()&&nums[j]==nums[j-1]){
j++;
}
}
i++;
while(i<nums.size()&&nums[i]==nums[i-1]){
i++;
}
}
return vv;
}
以上就是关于双指针算法的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!