【算法题】从0培养算法思想——双指针篇

本文介绍了双指针算法在解决经典问题中的应用,包括移动零问题、最大容器盛水以及三数之和,展示了如何通过对撞指针和快慢指针优化求解过程。
摘要由CSDN通过智能技术生成

   

目录

双指针

1.移动零问题

2. 称最多水的容器

 3.三数之和


   

       在这个专栏里我会收集一些很经典的算法题,并分享算法原理和题解,这里面每一道题或者带给了我新的思路,或者是代码简洁高效,或者题目在面试中出现的频率很高。我想把它们记录下来,大家有更好的思路也欢迎大家在评论区交流啊!

         欢迎大家交流!!!

         欢迎大家交流!!!

         欢迎大家交流!!!

双指针

常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。
对撞指针:⼀般⽤于顺序结构中,也称左右指针。
对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼
近。
对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循
环),也就是:
left == right (两个指针指向同⼀个位置)
left > right (两个指针错开)
快慢指针:⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。
这种⽅法对于处理环形链表或数组⾮常有⽤。
其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快
慢指针的思想。
快慢指针的实现⽅式有很多种,最常⽤的⼀种就是:
在⼀次循环中,每次让慢的指针向后移动⼀位,⽽快的指针往后移动两位,实现⼀快⼀慢。

1.移动零问题

题目链接:

https://leetcode.cn/problems/move-zeroes/description/

解题思路:

利用两个指针将数组分为三区间,分别是不含零元素的区间,全是零元素的区间,未扫描的区间。

代码实现: 

class Solution {
    public void moveZeroes(int[] nums) {
        int right=0,left=0;
        int length=nums.length;
        while(right<length){
         if(nums[right]!=0){
             int tmp=nums[right];
             nums[right]=nums[left];
             nums[left]=tmp;
             left++;
         }
         right++;
        }
    }
}

2. 称最多水的容器

题目链接:

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

解题思路:

设两个指针 left right 分别指向容器的左右两个端点,此时容器的容积 :
v = (right - left) * min( height[right], height[left])
容器的左边界为 height[left] ,右边界为 height[right]
为了⽅便叙述,我们假设「左边边界」⼩于「右边边界」。
如果此时我们固定⼀个边界,改变另⼀个边界,⽔的容积会有如下变化形式:
  • 容器的宽度⼀定变⼩。
  • 由于左边界较⼩,决定了⽔的⾼度。如果改变左边界,新的⽔⾯⾼度不确定,但是⼀定不会超过右边的柱⼦⾼度,因此容器的容积可能会增⼤。
  • 如果改变右边界,⽆论右边界移动到哪⾥,新的⽔⾯的⾼度⼀定不会超过左边界,也就是不会超过现在的⽔⾯⾼度,但是由于容器的宽度减⼩,因此容器的容积⼀定会变⼩的。
由此可⻅,左边界和其余边界的组合情况都可以舍去。所以我们可以 left++ 跳过这个边界,继
续去判断下⼀个左右边界。
当我们不断重复上述过程,每次都可以舍去⼤量不必要的枚举过程,直到 left right
遇。期间产⽣的所有的容积⾥⾯的最⼤值,就是最终答案。

代码实现:

class Solution {
    public int maxArea(int[] height) {
        int length=height.length;
        int left=0;
        int right=length-1;
        int max=0;
        int v=0;
        while(left!=right){
            if(height[left]<=height[right]){
                v=(right-left)*height[left];
                if(v>max){
                    max=v;
                }
                left++;
            }else{
                v=(right-left)*height[right];
               if(v>max){
                    max=v;
                }
                right--;
            }
        }
        return max;
    }
}

 3.三数之和

题目链接:

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

解题思路:

本题与两数之和类似,是⾮常经典的⾯试题。
与两数之和稍微不同的是,题⽬中要求找到所有「不重复」的三元组。那我们可以利⽤在两数之和
那⾥⽤的双指针思想,来对我们的暴⼒枚举做优化:
  •  先排序;
  •  然后固定⼀个数 a
  • 在这个数后⾯的区间内,使⽤「双指针算法」快速找到两个数之和等于 -a 即可。
但是要注意的是,这道题⾥⾯需要有「去重」操作
  • 找到⼀个结果之后, left right 指针要「跳过重复」的元素;
  • 当使⽤完⼀次双指针算法之后,固定的 a 也要「跳过重复」的元素。

代码实现: 

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
         Arrays.sort(nums);
         int length=nums.length;
         List<List<Integer>> list=new ArrayList<>();
         for(int i=0;i<length;){
             if(nums[i]>0){
                 return list;
             }
           int right=length-1;
           int left=i+1;
           int count=-nums[i];
            while(left<right){
                if(nums[left]+nums[right]==count){
                    list.add(new ArrayList<Integer>(Arrays.asList(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++;
                    }
                    
                }else if(nums[left]+nums[right]<count){
                    left++;
                }else{
                    right--;
                }
            }
                i++;
            while(i<length&&nums[i]==nums[i-1]){
                i++;
            }
         }
         return list;
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值