• 算法思想总结:双指针算法


    一、移动零

    . - 力扣(LeetCode) 移动零

    该题重要信息:1、保持非0元素的相对位置。2、原地对数组进行操作

     思路:双指针算法

    1. class Solution {
    2. public:
    3. void moveZeroes(vector<int>& nums)
    4. {
    5. int n=nums.size();
    6. for(int cur=0,des=-1;cur
    7. if(nums[cur])//如为非零,就要与des后面的位置元素进行交换
    8. swap(nums[++des],nums[cur]);
    9. }
    10. };

     二、复写零

    . - 力扣(LeetCode)复写零

    该题的重要信息:1、不要在超过该数组的长度的位置写入元素(就是不要越界)2、就地修改(就是不能创建新数组)。3、不返回任何东西。

     思路:双指针算法

    1. class Solution {
    2. public:
    3. void duplicateZeros(vector<int>& arr)
    4. {
    5. int cur=0,des=-1,n=arr.size();
    6. //找最后一个被复写数的位置
    7. for(;cur
    8. {
    9. if(arr[cur])
    10. ++des;
    11. else
    12. des+=2;
    13. if(des>=n-1)//要让des指向最后一个位置
    14. break;
    15. }
    16. //边界修正
    17. if(des==n)
    18. {
    19. arr[--des]=0;
    20. --des;
    21. --cur;
    22. }
    23. //从后往前复写
    24. for(;cur>=0;--cur)
    25. {
    26. if(arr[cur])
    27. arr[des--]=arr[cur];
    28. else
    29. {
    30. arr[des--]=0;
    31. arr[des--]=0;
    32. }
    33. }
    34. }
    35. };

     三、快乐数

    . - 力扣(LeetCode)快乐数

     该题的关键是:将正整数变成他的每位数的平方之和,有可能会一直循环始终到不了1,也有始终是1(快乐数)

    思路:快慢双指针算法

    以上的两个结论在博主的关于链表带环追击问题的文章里面有分析

    顺序表、链表相关OJ题(2)-CSDN博客

    1. class Solution {
    2. public:
    3. int bitsum(int n)
    4. {
    5. int sum=0;
    6. while(n)
    7. {
    8. int t=n%10;
    9. sum+=t*t;
    10. n/=10;//最后一位算完后拿掉
    11. }
    12. return sum;
    13. }
    14. bool isHappy(int n)
    15. {
    16. int slow=n,fast=bitsum(n);
    17. while(fast!=slow)
    18. {
    19. slow=bitsum(slow);
    20. fast=bitsum(bitsum(fast));
    21. }
    22. return slow==1;
    23. }
    24. };

    四、盛最多水的容器

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

    思路1、暴力枚举(时间复杂度太高)

    1. class Solution {
    2. public:
    3. int maxArea(vector<int>& height)
    4. {
    5. //暴力枚举
    6. int n=height.size();
    7. int ret=0;
    8. for(int i=0;i
    9. for(int j=i+1;j
    10. ret=max(ret,min(height[i],height[j])*(j-i));
    11. return ret;
    12. }
    13. };

    思路2、双指针对撞算法

    1. class Solution {
    2. public:
    3. int maxArea(vector<int>& height)
    4. {
    5. int left=0,right=height.size()-1,ret=0;
    6. while(left
    7. {
    8. ret=max(ret,min(height[left],height[right])*(right-left));
    9. if(height[left]
    10. ++left;
    11. else
    12. --right;
    13. }
    14. return ret;
    15. }
    16. };

    五、有效三角形的个数

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

     思路1:升序+暴力枚举

    思路2:升序+利用双指针算法

    1. class Solution {
    2. public:
    3. int triangleNumber(vector<int>& nums)
    4. {
    5. //排序一下
    6. sort(nums.begin(),nums.end());
    7. //先固定一个数,然后用双指针去找比较小的两个数
    8. int n=nums.size(),ret=0;
    9. for(int i=n-1;i>=2;--i)
    10. {
    11. int left=0,right=i-1;
    12. while(left
    13. {
    14. int sum=nums[left]+nums[right];
    15. if(sum<=nums[i]) ++left;
    16. else
    17. {
    18. ret+=(right-left);
    19. --right;
    20. }
    21. }
    22. }
    23. return ret;
    24. }
    25. };

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

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

     

    思路1:两层for循环找到所有组合去计算

    思路2:利用单调性,使用双指针算法解决问题

    1. class Solution {
    2. public:
    3. vector<int> twoSum(vector<int>& price, int target)
    4. {
    5. int n=price.size();
    6. int left=0,right=n-1;
    7. while(left
    8. {
    9. int sum=price[left]+price[right];
    10. if(sum>target) --right;
    11. else if(sum
    12. else return {price[left],price[right]};
    13. }
    14. return {1,0};
    15. }
    16. };

    七、三数之和

    . - 力扣(LeetCode)三数之和

    解法1:排序+暴力枚举+set去重

    解法2:排序+双指针

    1. class Solution {
    2. public:
    3. vectorint>> threeSum(vector<int>& nums)
    4. {
    5. vectorint>> ret;
    6. int n=nums.size();
    7. //先排序
    8. sort(nums.begin(),nums.end());
    9. //先固定一个数
    10. for(int i=0;i
    11. {
    12. if(nums[i]>0) break;//小优化
    13. int target =-nums[i];//目标值
    14. //定义双指针
    15. int left=i+1,right=n-1;
    16. while(left
    17. {
    18. int sum=nums[left]+nums[right];
    19. if(sum
    20. else if(sum>target) --right;
    21. else
    22. {
    23. ret.push_back({nums[left],nums[right],nums[i]});//插入进去
    24. ++left;
    25. --right;
    26. //去重
    27. while(left-1]) ++left;//去重要注意边界
    28. while(left1]) --right;
    29. }
    30. }
    31. ++i;
    32. while(i-1]) ++i;//去重要注意边界
    33. }
    34. return ret;
    35. }
    36. };

    八、四数之和

    . - 力扣(LeetCode)四数之和

    解法1:排序+暴力枚举+set去重

    解法2:排序+双指针(和上一题基本一样,无非就是多固定了一个数)

    1. class Solution {
    2. public:
    3. vectorint>> fourSum(vector<int>& nums, int target)
    4. {
    5. vectorint>> ret;
    6. //先进行排序
    7. sort(nums.begin(),nums.end());
    8. //利用双指针解决
    9. int n=nums.size();
    10. //先固定一个数
    11. for(int i=0;i
    12. {
    13. //再固定一个数
    14. for(int j=i+1;j
    15. {
    16. int left=j+1,right=n-1;
    17. long long aim=(long long)target-nums[i]-nums[j];//确保不超出范围
    18. while(left
    19. {
    20. long long sum=nums[left]+nums[right];
    21. if(sum
    22. else if(sum>aim) --right;
    23. else
    24. {
    25. ret.push_back({nums[i],nums[j],nums[left],nums[right]});
    26. ++left;
    27. --right;
    28. //去重
    29. while(left-1]) ++left;
    30. while(left1]) --right;
    31. }
    32. }
    33. //去重
    34. ++j;
    35. while(j-1]) ++j;
    36. }
    37. //去重
    38. ++i;
    39. while(i-1]) ++i;
    40. }
    41. return ret;
    42. }
    43. };

    九、总结

    常见的双指针有三种形式:前后指针、对撞指针、快慢指针

    1、前后指针:用于顺序结构,一般是两个指针同方向,cur指针用来遍历数组,des指针将数组进行区域划分。(如1、2题)

           注意事项:如果是从前往后遍历,要注意dst不能走得太快,否则cur还没遍历到一些数就会被覆盖掉,必要时候可以根据情况从后往前遍历。

    2、快慢指针:其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。
    这种⽅法对于处理环形链表或数组⾮常有⽤。(如第3题,以及链表带环的问题)

            注意事项: 其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快慢指针的思想。最常用的就是快指针走两步,慢指针走一步。

    3、对撞指针:一般用于顺序结构。从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。并且常常会用到单调性!!(如4-8题)
            注意事项:对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环)

    用双指针策略一般可以比暴力枚举降低一个次方的时间复杂度

         如果后面还有关双指针的经典题目,博主会继续在这篇更新的!!

  • 相关阅读:
    代码随想录算法训练营Day59 | 503.下一个更大元素II 42. 接雨水
    LeetCode·每日一题·952.按公因数计算最大组件大小·并查集
    prometheus安装和oracle告警配置
    rust学习-Arc
    windows10 vs2019 版本:cmake将 opencv_contrib-4.5.5 扩展模块编译添加到 opencv-4.5.5 正式版中
    一篇必读的物联网平台物模型开发指南,为你解锁未来科技趋势
    NPDP认证|制造业产品经理日常工作必备技能,快来学习提升吧!
    2022-git 如何切换分支
    log4js node日志插件
    MindSpore报错what(): scoped_acquire::dec_ref(): thread state must be current!
  • 原文地址:https://blog.csdn.net/weixin_51142926/article/details/136617340