【灵神基础算法精讲】01 相向双指针 两数之和 三数之和

两数之和

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        /* 解题思路:相向双指针
         * 令指针left和指针right分别指向当前最小和最大的两个数
         * 初始化:left = 0, right = n-1
         * 在每一轮循环中:
             计算sum = nums[left] + nums[right]
             (假设mid是nums[left]和nums[right]之间的任意数)
             判断sum ? target:
               sum == target: 找到解,return [left, right]
               sum > target:  nums[right]+mid必然大于target,不必再判断nums[right],right--;
               sum < target: nums[left]+mid必然小于target, 不必再判断nums[left],left++;
         * 退出循环的条件:while(left < right) loop;
         */
        
        // 初始化指针
        int left = 0; 
        int right = numbers.length - 1;
        
        // 循环移动双指针
        while(left < right){
            int sum = numbers[left] + numbers[right];
            if(sum == target){
                // 下标从1开始
                return new int[]{left+1, right+1};
            }else if(sum > target){
                right--;
            }else{
                left++;
            }
        }
        
        return null;
    }
}

三数之和

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        /* 实现思路:基本思路就是先排序,然后枚举i,在每一个i下转换为对j和k的两数之和问题
         * 如何避免重复解?
         *  1. 规定 i <= j <= k,即 nums[i] <= nums[j] <= nums[k]
         *  2. 当需要移动指针时,要移动到下一个不重复的数字
         */
        
        Arrays.sort(nums); // 对数组进行排序
        List<List<Integer>> ans = new ArrayList<>();
        int n = nums.length;
        int i = 0;
        while (i < n - 2) {
            int x = nums[i];
            // 优化一:如果「在当前i下sum能取到的最大值」小于0,说明当前i下不会有解了,跳过当前i
            if(x + nums[n - 2] + nums[n - 1] < 0) {
                i = moveI(nums, i);
                continue;
            }
            // 优化二:如果「sum能取到的最小值」大于0,说明之后不会有解了,直接结束循环
            if(x + nums[i + 1] + nums[i + 2] > 0){
                break;
            }
            // 在当前i下,将三数之和问题转换为对j和k的两数之和问题
            int j = i + 1, k = n - 1;
            while (j < k) {
                int s = x + nums[j] + nums[k];         
                if (s > 0) k = moveK(nums, j, k);
                else if (s < 0) j = moveJ(nums, j, k);
                else {
                    ans.add(List.of(x, nums[j], nums[k]));
                    j = moveJ(nums, j, k);
                    k = moveK(nums, j, k);
                }
            }
            i = moveI(nums, i);
        }
        return ans;
    }
    
    // 将指针I移动到下一个不重复的数字上
    private int moveI(int[] nums, int i){
        i++;
        while(i < nums.length - 2 && nums[i] == nums[i - 1]){
            i++;
        }
        return i;
    }
    
    // 将指针j移动到下一个不重复的数字上
    private int moveJ(int[] nums, int j, int k){
        j++;
        while(j < k && nums[j] == nums[j - 1]){
            j++;
        }
        return j;
    }
    
    // 将指针k移动到下一个不重复的数字上
    private int moveK(int[] nums, int j, int k){
        k--;
        while(k > j && nums[k] == nums[k + 1]){
            k--;
        }
        return k;
    }
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值