zoukankan      html  css  js  c++  java
  • 2SUM、3SUM、KSUM

    2SUM、3SUM、KSUM


    2SUM:

    Question:

    给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。

    你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

    Example:

    给定 nums = [2, 7, 11, 15], target = 9
    因为 nums[0] + nums[1] = 2 + 7 = 9
    所以返回 [0, 1]
    

    思路:
    1.暴力求解,遍历每一种可能,不予考虑。
    2.有没有什么办法够快速找到某一数的匹配序号而不用去遍历?Hash表。

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            HashMap<Integer,Integer> map=new HashMap<>();  //建立 值-位置 的键值对映射。
            for(int i=0;i<nums.length;i++){
                if(map.get(target-nums[i])!=null){
                    return new int[]{i,map.get(target-nums[i])}; //根据值找到位置
                }
                map.put(nums[i],i);
            }
            return null;
        }
    }
    

    3SUM

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0
    ?找出所有满足条件且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
    
    满足要求的三元组集合为:
    [
      [-1, 0, 1],
      [-1, -1, 2]
    ]
    

    思路:
    1.可以实现问题的转化,我们可以先确定单个元素,比如固定 nums[0],那我们的问题就变成了 target=-nums[0] 的2SUM问题。时间复杂度为O(n2)。
    2.双游标技巧:
    1.先将数组排序
    2.我们采用两个游标,一个从左向右移动,另一个从右向左移动。

    伪代码:
    if(num[left]+num[right]==0)  得到答案;左右游标同时按规定方向移动一个;
    else if(num[left]+num[right]<0) 左边游标右移一格;
    else if(num[left]+num[right]>0) 右边游标左移一格;
    
    实现代码:
    public List<List<Integer>> threeSum(int[] num) {
        Arrays.sort(num);
        List<List<Integer>> res = new LinkedList<>(); 
        for (int i = 0; i < num.length-2; i++) {
            if (i == 0 || (i > 0 && num[i] != num[i-1])) {
                int lo = i+1, hi = num.length-1, sum = 0 - num[i];
                while (lo < hi) {
                    if (num[lo] + num[hi] == sum) {
                        res.add(Arrays.asList(num[i], num[lo], num[hi]));
                        while (lo < hi && num[lo] == num[lo+1]) lo++;
                        while (lo < hi && num[hi] == num[hi-1]) hi--;
                        lo++; hi--;
                    } else if (num[lo] + num[hi] < sum) lo++;
                    else hi--;
               }
            }
        }
        return res;
    }
    

    这个问题读者最大的疑惑可能是会不会跳过了某些组合,答案是不会。
    本身数组有序,如果两个值相加 < -num[i] ,说明值过小,而右边的值是当前最大值,所以右游标向左的数值和左游标值相加必定<0,就不用考虑了,所以这时正确的做法,应该是将左边游标移动。
    同理,当两数相加 > -num[i] 时,就应该是右边游标左移一格了。
    当两数相加正好 = -num[i],那么就找到一个符合的组合。否则,i就取下一位。


    KSUM的通解算法:

    善于观察的同学应该已经发现,NSUM这种问题的一个通用的思想就是分治,4SUM可以降成3SUM,3SUM又可以激绛为2SUM,所以我们通过递归能够实现KSUM的解法。

    public List<List<Integer>> fourSum(int[] nums, int target) {
            Arrays.sort(nums);
            return kSum(nums, 0, 4, target);
        }
        private List<List<Integer>> kSum (int[] nums, int start, int k, int target) {
            int len = nums.length;
            List<List<Integer>> res = new ArrayList<List<Integer>>();
            if(k == 2) { //two pointers from left and right
                int left = start, right = len - 1;
                while(left < right) {
                    int sum = nums[left] + nums[right];
                    if(sum == target) {
                        List<Integer> path = new ArrayList<Integer>();
                        path.add(nums[left]);
                        path.add(nums[right]);
                        res.add(path);
                        while(left < right && nums[left] == nums[left + 1]) left++;
                        while(left < right && nums[right] == nums[right - 1]) right--;
                        left++;
                        right--;
                    } else if (sum < target) { //move left
                        left++;
                    } else { //move right
                        right--;
                    }
                }
            } else {
                for(int i = start; i < len - (k - 1); i++) {
                    if(i > start && nums[i] == nums[i - 1]) continue;
                    List<List<Integer>> temp = kSum(nums, i + 1, k - 1, target - nums[i]);
                    for(List<Integer> t : temp) {
                       t.add(0, nums[i]);
                    }                    
                    res.addAll(temp);
                }
            }
            return res;
        }
    
  • 相关阅读:
    多姿多彩的线程
    字典操作
    字符串语法
    购物车
    列表常用语法
    整数划分问题
    计算N的阶层
    判断是否是素数
    快速排序
    冒泡排序
  • 原文地址:https://www.cnblogs.com/gujiewei/p/9670570.html
Copyright © 2011-2022 走看看