专题一————双指针

一、移动零

1、使用场合

当一个题目给你一个类似与指针的结构,并要你做出类似于划分数组的行为,我们这个时候就可以使用双指针。

2、两个指针的作用

具体题目需要具体思考

cur : 一般是从左到右扫描数组,遍历数组。

dest :以及处理的区间,规定条件的最后一个位置。

3、例题

https://leetcode.cn/problems/move-zeroes/submissions/611244806/https://leetcode.cn/problems/move-zeroes/submissions/611244806/

(1)模块划分

        [0,dest]       [dest+1,cur-1]         [cur ,n-1]

                  处理过的                        待处理

(2)代码实现

          

class Solution {
public:
    void moveZeroes(vector<int>& nums)
    {
        int dest = -1;
        int cur = 0;
        while(cur<nums.size())
        {
            if (nums[cur] == 0)
            {
                cur++;
            }
            else
            {
                dest++;
                swap(nums[cur],nums[dest]);
                cur++;
            }
        }
                
            }
};

(3)代码解析

        cur : 一般是从左到右扫描数组,遍历数组。

        dest :以及处理的区间,零的最后一个位置。

        我们首先将cur定义在0的位置,dest定义在-1的位置,让cur遍历数组,如果是0,cur++

        不是零就让dest++,然后将dest和cur所指向的数组交换,以此来达到三个区间始终保持如下的          模式

                                [0,dest]       [dest+1,cur-1]         [cur ,n-1]

                                         处理过的                        待处理

二、移动零

https://leetcode.cn/problems/duplicate-zeros/description/https://leetcode.cn/problems/duplicate-zeros/description/

1、使用场合

类似与移动数组的操作都可以使用双指针

2、两个指针的作用

思路:如上图所示,我们需要对数组就地进行修改,但我们可以从异地双指针中寻找思路

一般情况下,我们是让cur遍历数组,des指向另外一个数组,当cur指向的字母是非零数字时,des复写一遍,当他时零时,des复写两遍,这样就解决了问题,我们可以把这样的思路应用在同一个数组中即:

但是如果我们遇到零复写两遍的时候,会覆盖后面的值,所以我们想到可以从前向后覆盖,让cur指向需要复写的最后一个字母,des指向最后一个字母

 总结:

1. 先找到最后一个“复写”的数

        双指针算法:

                                1. 先判断cur位置的值

                                2. 决定des向后移动一步还是两步

                                3. 判断一下des是否已经到结束为止

                                4.cur++;

2. 处理一下边界情况

        如果des在上一阶段越界,我们让cur--;des-=2;在进行下一步

3.从后向前完成复写操作

 代码:

class Solution {
public:
    void duplicateZeros(vector<int>& arr) 
    {
        int n = arr.size();
        int des = -1;
        int cur = 0;
         while(des< n-1)
         {
            if(arr[cur]) des++;
            else des += 2;
            if(des >= n-1) break;
              cur++;
         }
         if(des == n)
         {
            arr[n-1]=0;
            cur--;
            des-=2;
         }
         while(cur>=0)
         {
            if(arr[cur])
            {
                arr[des--]=arr[cur--];
            }
            else
            {
                arr[des--]=0;
                arr[des--]=0;
                 cur--;
            }
         }
    }
};

三、快乐数

1、题目

https://leetcode.cn/problems/happy-number/description/https://leetcode.cn/problems/happy-number/description/ 

2、题目解析

因为题目中说明了,无论是不是快乐数都会进入循环,唯一的不同是快乐数是循环的数是1,而非快乐数循环的不是1(其实无论有没有这句话,我们都可以通过推导知道,每一次将该数替换为它每个位置上的数字的平方和,都会进入循环例如我们举一个例子他的n = 9999999999,他的下一个数是 810,且他以后的数不可能比99999999999再大,所以他每次的值就会在1到810中出现,最多我们循环810次,我们就可以进入循环) 他就是带环问题

3、使用场合

当我们需要数组或者链表带环问题,我们可以使用快慢指针。至于为什么使用快慢指针以及快慢指针关于带环问题的其他应用,在下面的文章中有详细解说

https://blog.csdn.net/shsbyshdbdhjsk/article/details/143371820?fromshare=blogdetail&sharetype=blogdetail&sharerId=143371820&sharerefer=PC&sharesource=shsbyshdbdhjsk&sharefrom=from_linkhttps://blog.csdn.net/shsbyshdbdhjsk/article/details/143371820?fromshare=blogdetail&sharetype=blogdetail&sharerId=143371820&sharerefer=PC&sharesource=shsbyshdbdhjsk&sharefrom=from_link4、两个指针的用法

  下一个slow指向slow的下一个数据,下一个fast指向fast的下一个数据的下一个数据。

5、代码实现

class Solution
{  
        
  public:
  int NextNumber(int n)
        {
            int sum = 0;
                while(n)
                {
                    int t = n%10;
                    sum += t*t;
                    n/=10;
                }
                return sum;
        }
    bool isHappy(int n)
    {
      
            int slow = n;
            int fast = NextNumber(n);
            while(slow != fast)
            {
                slow = NextNumber(slow);
                fast = NextNumber(NextNumber(fast));
            }
            if(slow == 1)
            {
                return true;
            }
            else
            {
                return false;
            }
    }
};

四、盛水最多的问题

1、题目

https://leetcode.cn/problems/container-with-most-water/description/https://leetcode.cn/problems/container-with-most-water/description/

  

2、题目解析

这里给大家提供两种解法:
解法一:

暴力枚举,我们使用两层for循环,不断遍历使它得出最大的容积。(但是会超时)

解法二:
利用单调性,使用双指针来解决问题

我们可以看到他的高是他的较小值,他的低是他两者之间的距离 ,我们可以将两个指针放在一头一尾,这时候他的低是最大的,但指针往里面移动时,他的底就会缩小,只有他的高增大的时候,他的容积就会增大,所以我们让两个指针的最小的那一个往里面移动,如果容积增大就更新容积

3、代码实现

class Solution {
public:
    int maxArea(vector<int>& height)
    {
        int cur = 0;
        int des = height.size() -1;
        int v = 0;
        while(cur < des)
        {
            int v1 = min(height[cur],height[des]) * (des - cur);
             v = max(v,v1);
            if(height[cur]<height[des])
            {
                cur++;
            }
            else
              des--;
        }

        return v;
    }
};

五、有效三角形的个数

1、题目

https://leetcode.cn/problems/valid-triangle-number/description/https://leetcode.cn/problems/valid-triangle-number/description/

2、题目解析

这里给大家提供题目两种解题思路

解法一:通过三层for循环一个一个的遍历,到底有几个(但是时间会超时)

解法二:利用单调性,使用双指针算法来解决问题(最小两边的和大于第三边一定是三角形)

1、我们将他们进行排序,然后你会得到一个升序的顺序表

2、然后再固定一个数

3、再最大的数的左区间内,使用双指针,快速统计符合要求的三元组的个数

例如:

我们一般会遇到两种情况:

当right+left>固定数的时候

因为left是最小的数所以他right加他都可以大于固定数,那么我们知道有right-left个数都是大于固定数的,然后我们将right--

第二种当left+right<=固定值的时候,此时right为可遍历的最大的数都不可以实现三角形,就说明right前面的所有值加上left都不可以实现三角形,所以我们让left++;

当大于left大于right的时候让移动固定数。

代码实现

class Solution {
public:
    int triangleNumber(vector<int>& nums)
    {
        sort(nums.begin(),nums.end());
        int x = 0;
        for(int i = nums.size()-1; i>=2; i--)
        {
            int left = 0;
            int right = i-1;
            while(left<right)
            {
                if(nums[left]+nums[right]>nums[i])
                {
                    x+= right -left;
                    right--;
                }
                else
                {
                    left++;
                }

            }
        }
        return x;
        
    }
};

 六、和为targt的两个数

1、题目

https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/

2、题目解析

这道题和上一道题高度类似同样由两种解法

解法一:暴力解法,两层for循环,循环遍历直到找到何为target的数

解法二:利用单调性,使用双指针算法解决问题:因为该数组是升序的所以我们将指针定在一 头                  一 尾此时会出现三种情况 :两者相加大于targe因为left是最小的数,所以left以后的值都                不同和right加使之等于targe,所以right--;如果两者相加小于target因为right是最大的数说明                right之前的数都不能和left相加使之等于targe所以left++;如果等于target返回结果

3、代码实现

class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target)
    {
        int left = 0;
        int right = price.size()-1;
        while(left<right)
        {
            if(price[left]+price[right]>target)
            {
                right--;
            }
            
            else if(price[left]+price[right]<target)
            {
                left++;
            }
            else
            {
                return {price[left],price[right]};
            }
        }
        return {-1,-1};
    }
};

七、三数之和

1、题目

https://leetcode.cn/problems/3sum/description/https://leetcode.cn/problems/3sum/description/

2、 题目解析

这道题和前面几道有异曲同工之妙同样有两种解法

解法一:暴力解法:首先我们要进行排序(方便去重),然后在暴力枚举 ,再利用set去重

解法二:排序 + 双指针:首先排序,在固定一个数,再在该数后面的区间内,利用双指针算法快速找到两个和等于固定数的相反数即可(如何使用双指针同上题一样)。去重的第二种解法:找到一种结果之后,left和right指针要跳过重复元素,当使用完一次双指针算法之后,i也需要跳过重复元素,但是这种算法需要避免越界。

3、代码解答

class Solution
{
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end());
        vector<vector<int>> ret;
        for(int i = 0; i<nums.size(); i++)
        {
            while(i!=0&&(nums[i]==nums[i-1])&&(i<nums.size()-1))
            {
                i++;
            }
            int left = i+1;
            int right = nums.size()-1;
            int target = -nums[i];
            while(left < right)
            {
                if(nums[left]+nums[right]>target) right--;
                else if(nums[left]+nums[right]<target) left++;
                else 
                {
                    ret.push_back({nums[i],nums[left],nums[right]});
                    right--;
                    left++;
                    while(left<right && nums[right]==nums[right+1])
                      right--;
                    while(left<right && nums[left]==nums[left-1])
                        left++;
                }
            }

        }
        return ret;
        
    }
};

八、四数之和 

1、题目

https://leetcode.cn/problems/4sum/description/https://leetcode.cn/problems/4sum/description/

2、 题目解析

和上面一题十分相似,有两种解法,

解法一:暴力枚举:首先排序,然后四层for循环 在将结果放入set容器中

解法二:排序+双指针:1、依次固定一个数a 2、在a后面的区间内,在固定一个数b,在b后面的区间内一头一尾各固定双指针找到两个数,是这两个数的和等于target - a - b即可。

3、代码实现

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target)
    { 
        vector<vector<int>> ret;
        sort(nums.begin(),nums.end());
        for(int  a = 0 ; a < nums.size(); )
        {
            for(int b = a+1;b<nums.size();)
            {
                int left = b+1;
                int right = nums.size()-1;
                long long aim =(long long) target - nums[a] - nums[b];
                while(left<right)
                {
                    int sum = nums[left]+nums[right];
                    if((nums[left]+nums[right]) < aim)
                    {
                        left++;
                    }
                    else if((nums[left]+nums[right] )> aim)
                    {
                        right--;
                    }
                    else
                    {
                        ret.push_back({nums[a],nums[b],nums[left],nums[right]});
                        left++;
                        right--;
                        while(left<right&&nums[left]==nums[left-1]) left++;
                        while(left<right&&nums[right]==nums[right+1]) right--;
                    }

                }
                b++;
                while(b<nums.size()&&(nums[b]==nums[b-1])) b++;
            }
                 a++;
                while(a<nums.size()&&(nums[a]==nums[a-1])) a++;

        }    
        return ret;  
    }
};

 

                                                                               

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值