算法——双指针专题

一、移动零

1.链接

283. 移动零 - 力扣(LeetCode)

2.描述

3.思路

双指针,一个指针指向头,一个指针用来遍历数组,将非零的值依次往前放

4.参考代码

cpp 复制代码
class Solution {
public:
    void moveZeroes(vector<int>& nums) 
    {
        for(int i = 0,head = 0;i<nums.size();i++)
        {
            if(nums[i])
                swap(nums[head++],nums[i]);
        }
    }
};

二、复写零

1.链接

1089. 复写零 - 力扣(LeetCode)

2.描述

3.思路

比较容易想到的思路就是用cur去遍历数组,遇到0就0往后的数据整体后挪,但这种思路时间复杂度较高

这里提供一个更加高效的方法

1.先找到原数组中,最终会被复习的最后一个数字

先用指针不做任何操作的模拟一遍,从前往后的复写,一个指针cur用于遍历数组,一个指针dest用来找到最末尾会被复写的数字,但是,此时会有个问题,当最后一个数字为0时,会出现越界情况,因为0需要两个位置,cur指针会落到n的位置,此时需要对这种情况单独做一下处理

2.从后往前的复习

有了第一步的基础,此时的cur会落在数组末尾处,dest落在最后一个需要被复写的数字上,此时只需要从后往前的进行复写即可

4.参考代码

cpp 复制代码
class Solution {
public:
    void duplicateZeros(vector<int>& arr) 
    {
        int cur = 0;
        int dest = -1;
        int n = arr.size();

        while(cur<n)
        {
            if(arr[cur] == 0)
            {
                dest+=2;
            }
            else
            {
                dest++;
            }
            if(dest >=n-1)
                break;
            cur++;
        }
        //对可能出现的特殊情况进行处理
        if(dest == n)
        {
            arr[--dest] = 0;
            cur--;
            dest--;
        }
        while(cur>=0)
        {
            if(arr[cur] == 0)
            {
                arr[dest--] = 0;
                arr[dest--] = 0; 
                cur--;
            }
            else
            {
                arr[dest--] = arr[cur--];
            }
        }
    }
};

三、快乐数

1.链接

202. 快乐数 - 力扣(LeetCode)

2.描述

3.思路

根据题意,我们可以通过画图发现,任意一个数的最终结果都会陷入两种循环:

1.true:1->1->1->...

2.false:不同的数之间的死循环

证明:假设一个最大的数9999999999,它的平方和结果为9*9*10 = 810(实际没有那么大),我们可以认为当循环到810+1次时,一定会有重复的数字出现

此时,我们使用快慢指针去让一个指针跑得快,一个指针跑得慢一点,那么若是在一个循环中,则快指针一定会在某时刻追上慢指针,此时只需要判断该循环时上面情况的哪种即可

4.参考代码

cpp 复制代码
class Solution {
public:

    int happynum(int n)
    {
        int ret = 0;
        while(n)
        {
            int m = n%10;
            ret += m*m;
            n/=10;
        }
        return ret;
    }
    bool isHappy(int n) 
    {
        int fast = happynum(n);
        int slow = n;
        while(fast != slow)
        {
            fast = happynum(happynum(fast));
            slow = happynum(slow);
        }
        return fast == 1;
    }

四、盛水最多的容器

1.链接

11. 盛最多水的容器 - 力扣(LeetCode)

2.描述

3.思路

根据题目分析,要确定容积,首先我们想到的就是暴力求解,将每一个情况都穷举遍历一遍

capacity = min[ height [left] ,height [right] ] *(right - left)

但时间复杂度太高,我们可以通过对撞指针的方式,去减少不必要的枚举

分析可知,让left指针和right指针分别置于左右两边,当一边小于另一边时,我们挪动大的那一边往中间靠,容积高度不变(高度取决于小的那一边),而宽度变小,容积一定变小,所以将大的一边往中间靠的操作没有任何意义

因此,只需要每次都将小的那一边往中间靠,宽缩小,但高有可能增大,容积也会增大,直到两个指针相遇,此时在这些情况中取最大值就是最大容积

4.参考代码

cpp 复制代码
class Solution {
public:
    int maxArea(vector<int>& height) 
    {
        int left = 0;
        int right = height.size()-1;
        int capacity = -1;
        while(left<right)
        {
            capacity = max(capacity,min(height[left],height[right])*(right-left));
            height[left] < height[right] ? left++ : right--;
        }
        return capacity;
    }
};

五、有效三角形的个数

1.链接

611. 有效三角形的个数 - 力扣(LeetCode)

2.描述

3.思路

直接考虑,我们想到的就是三层for循环去穷举去所有的可能,在此思路下,我们尝试优化一下

我们知道三角形的两个最小的边之和一定会大于第三边,满足这个条件即可,因此,我们可以先给数组进行排序,然后设置一个所谓的最长边,然后然后用双指针去锁定最长边的前边部分

依次遍历数组中每个元素,即可求得一共有多少个有效三角形

4.参考代码

cpp 复制代码
class Solution {
public:
    int triangleNumber(vector<int>& nums) 
    {
        int n = nums.size();
        if(n < 3)
        {
            return 0;
        }
        sort(nums.begin(),nums.end());
        int ret = 0;
        for(int i = 2;i<n;i++)
        {
            int left = 0;
            int right = i-1;
            while(left < right)
            {
                if(nums[left]+nums[right] <= nums[i])
                {
                    left++;
                }
                else
                {
                    ret+=right-left;
                    right--;
                }
            }
        }
        return ret;
    }
};

六、查找总价值为目标值的两个商品

1.链接

LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)

2.描述

3.思路

不难想到,对撞指针,两个指针一左一右,相加值小于目标值则左边指针往右走,反之右边的往左走,直到找到相等的时刻返回即可,当两个指针相遇则没有满足条件的两个数

4.参考代码

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) 
    {
        int left = 0;
        int right = price.size()-1;
        vector<int> ret;
        while(left < right)
        {
            int sum = price[left] + price[right];
            if(sum < target)
            {
                left++;
            }
            else if(sum > target)
            {
                right--;
            }
            else if(sum == target)
            {
                ret.push_back(price[left]);
                ret.push_back(price[right]);
                break;
            }
        }
        return ret;
    }
};

七、三数之和

1.链接

15. 三数之和 - 力扣(LeetCode)

2.描述

3.思路

4.参考代码

cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        int n = nums.size();
        int cur = 0;
        sort(nums.begin(),nums.end());
        vector<vector<int>> ret;
        while(cur < n)
        {
            int left = cur+1;
            int right = n-1;
            int x = -nums[cur];
            while(left < right)
            {
                int sum = nums[left] + nums[right];
                if(sum < x)
                {
                    left++;
                }    
                else if (sum > x)
                {
                    right--;
                }
                else
                {
                    ret.push_back({nums[cur],nums[left++],nums[right--]});
                    //去重
                    while(left < right && nums[left] == nums[left-1])
                        left++;
                    while(left < right && nums[right] == nums[right+1])
                        right--;
                }
            }
            cur++;
            while(cur < n && nums[cur] == nums[cur-1]) cur++;
        }
        return ret;
    }
};

八、四数之和

1.链接

18. 四数之和 - 力扣(LeetCode)

2.描述

3.思路

思路和三数之和的思路是一样的,我们可以先固定一个数a,然后将其后面的部分用三数之和的思路去解决,找到target-a的值,思路上不难,但细节上需要有很多需要处理

关键在于去重有四个位置:

1.left和right找到后,需要对这两个去进行去重

2.当nums[ j ]找完往后走时需要去重

3.当nums[ i ]找完往后走时需要去重

4.参考代码

cpp 复制代码
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        int n = nums.size();
        vector<vector<int>> ret;
        sort(nums.begin(),nums.end());
        for(int i = 0 ;i<n;)//先固定住一个数,然后后面的位置进行三数之和的思路去找到target-nums[i]
        {
            for(int j = i+1 ; j<n;)
            {
                int left = j+1;
                int right = n-1;
                while(left < right)//找到target-nums[i] - nums[j]的两数之和
                {
                    long long sum = (long long)nums[left] + (long long)nums[right];
                    long long des = (long long)target - nums[i] - nums[j];
                    if(sum < des)
                    {
                        left++;
                    }
                    else if(sum > des)
                    {
                        right--;
                    }
                    else
                    {
                        ret.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 < n && nums[j] == nums[j-1]) j++;
            }
            //去重
            i++;
            while(i < n && nums[i] == nums[i-1]) i++;           
        }
        return ret;
    }
};

5.代码分析

总结

本篇总结了部分经典的关于双指针的算法题,其中有"对撞指针""快慢指针"等等的思想和例题

相关推荐
迷迭所归处几秒前
动态规划 —— 子数组系列-单词拆分
算法·动态规划
爱吃烤鸡翅的酸菜鱼1 分钟前
Java算法OJ(8)随机选择算法
java·数据结构·算法·排序算法
hunandede29 分钟前
av_image_get_buffer_size 和 av_image_fill_arrays
c++
寻找码源44 分钟前
【头歌实训:利用kmp算法求子串在主串中不重叠出现的次数】
c语言·数据结构·算法·字符串·kmp
Matlab精灵1 小时前
Matlab科研绘图:自定义内置多款配色函数
算法·matlab
诚丞成1 小时前
滑动窗口篇——如行云流水般的高效解法与智能之道(1)
算法
手握风云-1 小时前
数据结构(Java版)第二期:包装类和泛型
java·开发语言·数据结构
怀澈1222 小时前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming19872 小时前
STL关联式容器之set
开发语言·c++
带多刺的玫瑰2 小时前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法