双指针算法总结

双指针算法大致有以下几个类型

  • 对撞指针:一般用来处理两数和问题
  • 快慢指针: 一般在链表中用的比较多,如求链表中间结点,链表是否有环等,当然一些非链表题也会用到相关的思想
  • 区间划分: 将数组分成两个不同性质的两个部分

 一、对撞指针

1.盛⽔最多的容器(点击这里,直达题目)

双指针算法总结_第1张图片

 看到这题,最直接的想法就是暴力,两层for循环直接枚举左右端点,在所有的情况中找到最大值,时间复杂度是O(n^2),过不了,那么我们怎么优化时间复杂度?

双指针算法总结_第2张图片

这两个问题就是双指针思路的关键,这里就解释一下为什么是方案1,因为S=h * w,而此时的w只能减小,那么我们如果想让S变大,就只能将h变大,而h是由两边较低的高度决定的,如果我们移动较高的那条边,那么h就会受限于较低的那条边的高度,所以S只会变小,这就导致我们只能移动较低的那条边,而这也就能保证这个算法的正确性,即我们是将没有枚举到的情况排除了,并不是说枚举漏了,代码如下

class Solution {
public:
    int maxArea(vector& height) {
        int left=0,right=height.size()-1,ans=0;
        while(left

2.和为s的两个数字(点击这里,直达题目)

 双指针算法总结_第3张图片

 这题当然也是可以直接暴力枚举,两层for循环结束,时间复杂度是O(n^2),但是题目中给的排序条件我们没有用上,那么我们如何利用这个条件优化时间复杂度?双指针算法总结_第4张图片

选方案2,理由如下

因为s=17,而target=9,所以我们需要让s变小,而中间的元素都比nums[left]大,比nums[right]小,如果移动left,只能让s更大,所以我们移动right,让s减小,如果遇到s数组有序,(思路的正确性的分析同上一题)

代码如下

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        int left=0,right=nums.size()-1;
        while(left

当然我们还能求starget的数字对个数等等,同样的分析方法,就留给大家做练习了。

总结一下:能否使用对撞指针,主要看是否满足"单调性",即在固定其中一个指针的情况下,left和right从逻辑上来说只能有一个发生移动,当我们不确定时,就照着上面的分析方法试试看

二、快慢指针

这个一般在链表中用的比较多,很多链表的经典题目都会用到,这里就不细讲在链表中的应用了,一般链表的题目大家都很容易想到这个思想,但是一旦题目脱离了链表,我们基本就不会有意识的想到这种思想,如下面这道题

快乐数(点击这里,直达题目)

双指针算法总结_第5张图片

 这道题的关键就在于无限循环因为如果你对链表很熟悉的话,你就会想到链表有环的相关问题,然后你就会想到快慢指针,而这就是这道题的解法

好,我们来看一下这道题,就是判断数是否是快乐数,如果是,那么在经过一段重复的操作之后,会变为1,如果不是,则会一直循环,本质不是和判断链表是否有环一摸一样的问题吗?没环,则链表会走到NULL,有环,则一直循环。至于有人说的无限循环,也可能是走一直不重复的数字,这个问题后面会简单证明一下,这里就先默认要么为1,要么循环

代码如下

class Solution {
public:
    int Get(int x){
        int res=0;
        while(x){
            res+=(x%10)*(x%10);
            x/=10;
        }
        return res;
    }
    bool isHappy(int n) {
        int fast=Get(n),slow=n;
        while(fast!=slow)//如果相等停下来,1也可以看成循环,只是循环的数字一直是1
        {
            slow=Get(slow);//相当于链表中slow往后走一步
            fast=Get(Get(fast));//相当于链表中fast往后走两步
        }
        return slow==1;//判断是否是快乐数
    }
};

双指针算法总结_第6张图片

 总结一下:快慢指针的思想不是只能出现在链表中,我们要能通过题目给的信息找出它和链表题之间的关系,实现知识的迁移变形

三、区间划分

移动零(点击这里,直达题目)

双指针算法总结_第7张图片

 我们的思路是根据题目要求维护三个区间,被双指针分割出来的三个区间

 双指针算法总结_第8张图片

 代码如下

class Solution {
public:
    void moveZeroes(vector& nums) {
        for(int i=0,j=0;j

这题的思路其实和快排中,选定目标值target,然后将数组分为<=target和>target两部分的实现一样,都是划分数组,维护区间,忘记的可以去回顾一下

总结一下:当遇到要将数组划分区间的题目时,可以想想能不能用双指针来做

当然这题用快慢指针的思想也能做,代码如下

class Solution {
public:
    void moveZeroes(vector& nums) {
        int j=0;
        for(int i=0;i

你可能感兴趣的:(算法,c++,leetcode)