算法——双指针之双数之和,三数之和,四数之和

26 篇文章 0 订阅

6.和为s的两个数

题目:. - 力扣(LeetCode)

此题暴力解法还是暴力枚举,即将所有的情况一一列举进行判断;

我们在暴力枚举的基础上进行优化:

题目已知数组有序,我们可以利用双指针来解决这类问题:

当l的元素加上r的元素时,无非只有三种情况:

(1) l + r >targrt:

如上图:8 + 66 > 61,那么由于是递增数列.从8 ~ 66之间的数加上66就一定大于61,所以66与其之前的任何一个数相加一定都大于61,那么66这个数就不用判断,直接right--;

(2) l + r < target:

8 + 34小于61,那么8 加上 8 ~ 34之间的数一定都小于61,那么就不用一一枚举,直接left++即可

(3) l + r == target:

直接返回即可

题解:

 class Solution {
     public int[] twoSum(int[] price, int target) {
        int left = 0;
        int right = price.length - 1;
        int[] ret = new int[2];
        while(left < right){
            int sum = price[left] + price[right];
            if(sum < target){
                left++;
            } else if(sum > target){
                right--;
            }else{
                ret[0] = price[left];
                ret[1] = price[right];
                break;
            }
        }
        return ret;
     }
 }

7.三数之和为0

题目:. - 力扣(LeetCode)

同样,i此题的暴力解法就是一一枚举,每次比较三个数的和与target的值是否相等,但是时间复杂度来到O(n^3),再加上题目要求的去重,时间复杂度就很大;

但是我们可以在暴力枚举的方法上进行优化:

(1)我们可以先将数组进行排序,以便后面舍去一些没必要枚举的

(2)前面我们做过两数之和的题目,那么很容易想到的就是:先固定一个数,在这个数之后,找到两数之和为这个数的相反数的结果即可,那么就用双指针算法

(3)不漏,与两数和题目不同的是:我们找到和为-target的两个数后,不该停止,而是left继续++,right继续--,直到二者相遇

(4)去重:

当我们找到一组后,那么再left继续++和right继续--时,如果遇到和之前相同的数,那么直接跳过即可,因为遇到和之前相同的数就一定会重复

并且:

当我们固定好-1并且找到结果后,会发现下一个数还是-1,那么如果继续枚举必然会出现重复的情况,因此也要跳过

(5)我们会发现,当数组是递增的时候,如果target是整数,那么target后面的数必定是正数,那么不可能会有两数之和等于-target的情况,那么后面就都不用考虑了

题解:

class Solution {
    public static List<List<Integer>> threeSum(int[] nums){
        Arrays.sort(nums);
        int n = nums.length;
        List<List<Integer>> ret = new ArrayList<>();
        for(int i = 0; i < n - 2;){
            if(nums[i] > 0){
                break;
            }
            int left = i + 1;
            int right = n - 1;
            int targer = -nums[i];
            while(left < right){
                if(nums[left] + nums[right] < targer){
                    left++;
                } else if (nums[left] + nums[right] > targer) {
                    right--;
                }else{
                    ret.add(new ArrayList<Integer>(Arrays.asList(nums[left],nums[right],nums[i])));
                    left++;
                    right--;
                    while(left < right && nums[left] == nums[left-1]){
                        left++;
                    }
                    while(left < right && nums[right] == nums[right+1]){
                        right--;
                    }
                }
            }
            i++;
            while (nums[i] == nums[i-1] && i < n - 2){
                i++;
            }
        }
        return ret;
    }
}

8.四数之和

题目:. - 力扣(LeetCode)

同样:暴力枚举的方法很容易想到,但是时间复杂度达到O(n^4)

我们结合三数之和的思想,可以先固定一个数,再固定一个数,接着利用双指针进行双数之和查找

那么剩下的只是代码问题:

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> ret = new ArrayList<>();
        int n = nums.length;
        for(int i = 0; i < n - 3;){
            for(int j = i + 1; j < n - 2;){
                int left = j+1;
                int right = n - 1;
                int t = target - nums[i] - nums[j];
                while(left < right){
                    long sum = nums[left] + nums[right];
                    if(sum < t){
                        left++;
                    }else if(sum > t){
                        right--;
                    }else{
                        ret.add(Arrays.asList(nums[left],nums[right],nums[i],nums[j]));
                        left++;
                        right--;
                        while(nums[left] == nums[left-1] && left < right){
                            left++;
                        }
                        while(nums[right] == nums[right+1] && left < right){
                            right--;
                        }
                    }
                }
                j++;
                while(nums[j] == nums[j-1] && j < n-2){
                    j++;
                }
            }
                i++;
                while(nums[i] == nums[i-1] && i < n-3){
                    i++;
                }
        }
        return ret;
    }
}

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值