Leetcode 双指针法

274 阅读7分钟

什么是双指针

在leetcode中 有一类题 可以采用双指针的方法:

  不同于普通的遍历,使用单指针(单循环)的方法进行遍历,而是采用双指针(双循环)的方法进行相应的遍历,从而达到相应的目的
  在使用的时候,我们通常设定左右两边指针向中间移动,亦或者在一边两个指针进行相应的移动。需要结合实际情况和场景进行相应 的选择,以及变通
  虽然在上面一直在强调指针,但其实在Java中并没有指针的概念,随之而来的是索引,这一点还是说清楚。

说了那么多直接来看看例子叭

双指针Leetcode例题

1.盛水最多的容器

avatar

思路:

  这道题是一道双指针类型的题目,分析题意决定盛水多少的量其实一直是短板决定的,我们需要找到的是一个较长的板和一个较短的板,同时两者的距离相对较长这也就可以构造出一个盛水最多的容器。
  当开始做这道题的时候,我们分别在左右0以及nums.length-1处各放置一个指针分别为L以及R;然后我们记录下当前的面积;然后我们把此时的L与R对比长度,永远移动的是小的那块板,并且将新得到的面积与以前记录的面积进行相应的比较,永远取大的那一块,这样子最终当L==R的时候,此时面积为最大。

代码实现:
class Solution {public int maxArea(int[] height) {
    //最小的短板决定了 你能装多少水
    int left = 0, right = height.length-1, maxSquare = 0;
    //开始循环
    while(left<right){
        maxSquare =height[right]<height[left] ?
                Math.max(maxSquare,(right-left)*height[right--]):
                Math.max(maxSquare,(right-left)*height[left++]);
    }
    return maxSquare;
}
}

2.三数之和

avatar

思路:

  这道题同样也是一道典型的双指针题目
  当开始做这道题的时候,我们需要先对杂乱无章的数组nums[]进行排序Arrays.sort();
  排序后的数组利用一个for循环,以及分别在i+1和nums.length位置的两个左右指针进行相应的移动,当sum>0的时候我们需要把最大值调小就是把num.length的指针--,当sum<0的时候需要把i+1位置的指针进行++
  随时记录满足条件的三元组,但在i+1以及nums.length的地方我们需要注意去重的操作
while (L<R&&nums[L]==nums[L+1]) L++;//去重
while (L<R&&nums[R]==nums[R-1]) R--;//去重
同时当i>0的时候我们需要对i+1和i相比较,如果等于的话continue这次循环就没必要了

代码
class Solution {
   public List<List<Integer>> threeSum(int[] nums) {
       //先进行数组排一下序
       Arrays.sort(nums);
       //定义返回结果集
       List<List<Integer>> resultList = new ArrayList<>();
       //如果nums 小于0的话要直接返回的哦 否则会报奇怪的异常的
       if (nums.length<3) return resultList;
       for (int i=0;i<nums.length;i++){
           //如果第一个数都已经大于0了 我们已经经过了排序 还这样 那证明最后的结果 不会出现0的和了
           if (nums[i]>0) return resultList;
           //还需要 在外面判定一下是否重复
           if (i>0&&nums[i]==nums[i-1]) continue;//判重
           //定义一下左指针和右指针
           int L = i+1;
           int R = nums.length-1;
           //进行左右指针的移动
           while (L<R){
               int sums = nums[i]+nums[L]+nums[R];
               if (sums == 0){
                   resultList.add(Arrays.asList(nums[i],nums[L],nums[R]));
                   while (L<R&&nums[L]==nums[L+1]) L++;//去重
                   while (L<R&&nums[R]==nums[R-1]) R--;//去重
                   L++;//这个jiajia跟上面得不一样!!!!
                   R--;
               }
               //根据sums得值调整双指针得位置
               else if (sums>0) R--;
               else if (sums<0) L++;
           }
       }
       return resultList;
   }
}

3.最接近的三数之和

avator

思路:

  最接近的三数之和,首先需要的也是先对于杂乱的nums[]进行相应的排序,利用Arrays.sort();相应的排序,得到一个递增的数组,与上面一道题的思路几乎一致。设置三个指针,第一个为i位置,第二个为i+1位置,第三个在nums[].length-1的位置。然后我们取一个sum=nums[0]+nums[1]+nums[2];随后我们移动左右指针,去寻找最接近的差值通过求取绝对值进行比较,利用Math.abs()。最后我们返回取到的三个值即可

代码:
class Solution {
   public int threeSumClosest(int[] nums, int target) {
       //先对数组排序
       Arrays.sort(nums);
       //先得到一个基准得和
       int sum = nums[0]+nums[1]+nums[2];
       for (int i =0;i<nums.length;i++){
           //定义左右指针
           int L = i+1;
           int R = nums.length-1;
           //进行双指针的移动
           while (L<R){
               int dValueSum = nums[i]+nums[L]+nums[R];
               //这个if为储值得过程
               if (Math.abs(dValueSum-target)<Math.abs(sum-target)) sum = dValueSum;//谁离target近就换成谁
               //下面都是移动指针的操作
               if (dValueSum>target) R--;
               else if (dValueSum<target) L++;
               else return sum;
           }
       }
       return sum;
   }
}

4.四数之和

avator

思路

  这次需要设置四个指针,但经常移动的还是双指针。我们需要分别在i i+1 i+2 nums.length-1设置四个指针
1.如果本nums[].length的长度小于四,就没必要开始这次的循环
2.首先如果在k>0的情况下,即已经完成第一次循环的基础上,如果此时的nums[k-1]==nums[k]就证明需要continue,因为重复   了。还有一种情况,我们的sum此时最大能取到的是nums[k]+nums[length-1]+nums[length-2]+nums[length-3],如果说这个值都小 于  target,那证明这次的循环也没必要进行,直接进行下次就行。此时我们同样可以得到整个数组中可以取到的最小的和,其中我  们可以得知sum的值为nums[k]+nums[k+1]+nums[k+2]+nums[k+3]如果着这个最小值都小于target那没必要循环了 直接break
3.第二个指针位于k+1的位置,首先也是当k+1>0我们进行相同于第二步的去重操作,同时我们建立所对应的左右指针,分别处于   k+2以及nums[].length-1的位置,分别命名为L和R指针此时的最小值为nums[k]+nums[k+1]+nums[L]+nums[L+1],如果小于我们    break,也能取得本次循环的最大值nums[k]+nums[k+1] + nums[R] + nums[R-1];如果target大于sum continue。
4.移动左右指针,将符合的记录下来,同时每次移动指针进行与target比较前,先去重

代码
class Solution {
       public List<List<Integer>> fourSum(int[] nums, int target) {
        //建立一个结果的集合类
        List<List<Integer>> resultList = new ArrayList<>();
        //建立一个数组长度int
        int len = nums.length ;
        //如果数组的长度小于4的话 就返回 这个结果集
        if ( nums == null||len < 4 ) {
            return resultList;
        }
        //对数组进行排序
        Arrays.sort(nums);
        //进行第一层循环 建立指针k
        for (int k = 0; k < len - 3; k++) {
            //去重操作
            if ( k > 0 && nums[k] == nums[k - 1] ) continue;
            //得到最大值 如果说target大于这个最大值就没必要这次的循环
            int maxK = nums[k] + nums[len-1] + nums[len - 2] + nums[len - 3];
            if ( target > maxK ) continue;
            //得到这个数组的最小的和 如果说target小于这个最小值 整个循环都可以跳出了
            int minK = nums[k] + nums[k + 1] + nums[k + 2] + nums[k + 3];
            if ( target < minK ) break;
            //进行第二次循环操作建立指针i
            for (int i = k + 1; i < len - 2; i++) {
                //去重操作
                if ( i > k + 1 && nums[i] == nums[i - 1] ) continue;
                //建立左右指针
                int L = i + 1;
                int R = len-1;
                //得到当前的最大值 如果说target大于这个最大值就没必要这次的循环
                int maxI = nums[k] + nums[i] + nums[R] + nums[R-1];
                if ( target > maxI ) continue;
                //得到最小值
                int minI = nums[k] + nums[i] + nums[L] + nums[L + 1];
                if ( target < minI ) break;
                while (L < R) {
                    //移动指针的值记录下来
                    int ans = nums[k] + nums[i] + nums[L] + nums[R];
                    //如果相等的话 就记录下来
                    if ( ans == target ) {
                        resultList.add(Arrays.asList(nums[k], nums[i], nums[L], nums[R]));
                        L++;
                        while (L < R && nums[L] == nums[L - 1]) L++;
                        R--;
                        while (L < R && nums[R] == nums[R + 1]) R--;
                    } else if ( ans > target ) {
                        R--;
                    } else {
                        L++;
                    }
                }
            }
        }
        return resultList;
    }
    

总结

 这四道题,尤其后面三道有递进的感觉,都直接或者间接采用了双指针的方法,记录一下自己的思路希望能给自己以及他人带来帮助