zoukankan      html  css  js  c++  java
  • kSum问题总结

    [LeetCode]1. Two Sum

    题目(题目已修改,和原题要求不一样)

    Given an array of integers, return indices of the two numbers such that they add up to a specific target.
    
    You may assume that each input would have exactly one solution, and you may not use the same element twice.
    

    测试案例

    Given nums = [2, 7, 11, 15], target = 9,
    
    Because nums[0] + nums[1] = 2 + 7 = 9,
    return [0, 1].
    

    思路一

    1. 先将数组排序。
    2. 从左至右依次遍历每个元素,同时在其右边的子序列中采用二分法查找 target 与当前元素的差值。

    代码如下

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int n = nums.length, pos;
            int[] res = new int[2];
            Arrays.sort(nums);
            for(int i = 0; i < n - 1; i++){
                if((pos = Arrays.binarySearch(nums, i + 1, n, target - nums[i])) > -1){
                    res[0] = nums[i];
                    res[1] = nums[pos];
                    break;
                }
            }
            return res;
        }
    }
    

    思路二

    1. 先将数组排序
    2. 从左至右依次遍历每个元素,同时在其右边的子序列中采用二分法查找 target 与当前元素的差值。由于元素值依次递增,从而,下次二分查找的范围必定在上次二分查找的范围内。所以,可以保存上次二分查找返回的下标。作为下次二分查找的右边界。

    代码如下

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int n = nums.length, pos = n;
            int[] res = new int[2];
            Arrays.sort(nums);
            for(int i = 0; i < n - 1; i++){
                if((pos = Arrays.binarySearch(nums, i + 1, pos, target - nums[i])) > -1){
                    res[0] = nums[i];
                    res[1] = nums[pos];
                    break;
                }
                //当pos = i + 1 时,说明后面的元素均不满足
                else if((pos = -(pos + 1)) == i + 1){
                    break;
                }
            }
            return res;
        }
    }
    

    思路三

    1. 先将数组排序。

    2. 定义两个下标,start 和 end,初始时,(start = 0, end = n - 1)

    3. 比较 start 和 end 位置处的元素之和与 target 的大小。

      当 nums[start] + nums[end] == target 时,循环结束,找到结果。

      当 nums[start] + nums[end] < target 时,start++。

      当 nums[start] + nums[end] > target 时,end--。

    代码如下

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            int n = nums.length, temp, start = 0, end = n - 1;
            int[] res = new int[2];
            Arrays.sort(nums);
            while(start < end){
                if((temp = nums[start] + nums[end]) > target){
                    end--;
                }
                else if(temp == target){
                    res[0] = nums[start];
                    res[1] = nums[end];
                    break;
                }
                else{
                    start++;
                }
            }
            return res;
        }
    }
    

    [LeetCode 15] 3Sum

    题目

    //题目被简化掉了返回所有组合的结果,仅返回一组。原题在后面
    Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0?
    

    测试案例

    Given array nums = [-1, 0, 1, 2, -1, -4],
    A solution set is: [-1, 0, 1]
    

    思路

    利用2Sum,先将数组进行排序,然后从左至右依次遍历每个元素,对于每个元素,在其右边的子数组中调用2Sum。

    代码如下

    class Solution {
        public List<Integer> threeSum(int[] nums) {
            int n = nums.length;
            Arrays.sort(nums);
            List<Integer> res = null;
            for(int i = 0; i < n -2; i++){
                if((res = twoSum(nums, i + 1, n - 1, -nums[i])) != null){
                    res.add(nums[i]);
                    break;
                }
            }
            return res;
        }
        List<Integer> twoSum(int[] nums, int start, int end, int target){
            int temp;
            List<Integer> res = null;
            while(start < end){
                if((temp = nums[start] + nums[end]) < target){
                    start++;
                }
                else if(temp > target){
                    end--;
                }
                else{
                    res = new ArrayList<>(3);
                    res.add(nums[start]);
                    res.add(nums[end]);
                    break;
                }
            }
            return res;
        }
    }
    

    原题

    Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
    
    Note:
    The solution set must not contain duplicate triplets.
    

    测试案例

    Given array nums = [-1, 0, 1, 2, -1, -4],
    
    A solution set is:
    [
      [-1, 0, 1],
      [-1, -1, 2]
    ]
    

    思路

    1. 在上面3sum的基础上输出所有结果,同时要去重。
    2. 去重的关键在于从左至右遍历每个元素时,如果与前面的元素相同,则不考虑当前元素。
    3. 同时为了输出所有结果,在2sum中,也需要输出所有满足条件的结果及去重。
    4. 在2sum中输出所有结果的思路是:当找到一对结果时,左右下标同时移动。
    5. 在2sum中去重的思路是:每次下标移动时,如果和前继元素相同,则继续移动。

    这是常用的去重思路。

    代码如下

    class Solution {
        public List<List<Integer>> threeSum(int[] nums) {
            int n = nums.length;
            List<List<Integer>> res = new LinkedList<>(), temp;
            Arrays.sort(nums);
            for(int i = 0; i < n - 2; i++){
                //3sum去重
                if(i == 0 || nums[i] != nums[i - 1]){
                    res.addAll(twoSum(nums, i + 1, n - 1, -nums[i]));
                }
            }
            return res;
        }
        List<List<Integer>> twoSum(int[] nums, int start, int end, int target){
            int temp;
            List<Integer> list;
            List<List<Integer>> res = new LinkedList<>();
            while(start < end){
                if((temp = nums[start] + nums[end]) == target){
                    list = new ArrayList<>(3);
                    list.add(nums[start]);
                    list.add(nums[end]);
                    list.add(-target);
                    res.add(list);
                }
                //2sum去重
                if(temp <= target){
                    while(++start < end && nums[start] == nums[start - 1]);
                }
                //2sum去重
                if(temp >= target){
                    while(--end > start && nums[end] == nums[end + 1]);
                }
            }
            return res;
        }
    }
    

    [LeetCode 18] 4Sum

    题目

    Given an array nums of n integers and an integer target, are there elements a, b, c, and d in nums such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
    
    Note:
    The solution set must not contain duplicate quadruplets.
    

    测试案例

    Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.
    
    A solution set is:
    [
      [-1,  0, 0, 1],
      [-2, -1, 1, 2],
      [-2,  0, 0, 2]
    ]
    

    思路1

    仿照3sum的思路,在3sum外面加一个循环。时间复杂度为 (O(n^3))

    代码

    class Solution {
        List<List<Integer>> result = new LinkedList<>();
        public List<List<Integer>> fourSum(int[] nums, int target) {
            Arrays.sort(nums);        
            int length = nums.length, pre;
            for(int i = 0;i < length - 3; i++){
                if(i > 0 && nums[i] == nums[i - 1]){
                    continue;
                }
                threeSum(nums,target - nums[i], i + 1, nums[i]);
            }
            return result;
        }    
        public void threeSum(int[] nums, int target, int start, int num1){
            int n = nums.length;        
            for(int i = start; i < n - 2; i++){
                if(i > start && nums[i] == nums[i - 1]){
                    continue;
                }
                twosum(nums, i + 1, target - nums[i], num1, nums[i]);
            }        
        }
        void twosum(int[] nums, int start, int target, int num1, int num2){
            int temp, end = nums.length - 1;
            List<Integer> list;
            while(start < end){
                if((temp = nums[start] + nums[end]) == target){
                    list = new ArrayList<>(4);
                    list.add(num1);
                    list.add(num2);
                    list.add(nums[start]);
                    list.add(nums[end]);
                    result.add(list);
                }
                if(temp >= target){
                    while(--end > start && nums[end] == nums[end + 1]);
                }
                if(temp <= target){
                    while(++start < end && nums[start] == nums[start - 1]);
                }
            }
        }
    }
    

    思路2

    1. 将任意两点的和作为key,value为一个 list ,里面存放和值相同的两点的下标,放入 HashMap 中。时间复杂度为 (O(n^2))
    2. 然后两层循环遍历每一对元素,从 target - 和值 对应的 value 中找出满足要求的结点。在 list 中查找的时间复杂度未知。

    kSum

    题目

    Given an unsorted array, determine if there are K elements that sum up to SUM.
    

    思路1

    延续上面思路,在 2sum 外面加 (k - 2) 层循环,时间复杂度为 (O(n^{k - 1}))

    思路2

    将 ksum 问题看作一个二维背包问题。一个约束为 sum,另一个约束为 k。且要求出同时满足的所有所有情况。

    代码如下

    class Solution{
        int min;
        int n;
        boolean[][][] record;
        LinkedList<Integer> stack = new LinkedList<>();
        List<List<Integer>> res = new LinkedList<>();
        public List<List<Integer>> kSum(int[] nums, int k, int sum){
            n = nums.length;
            Arrays.sort(nums);
            min = nums[0];
            sum -= min * k;
            //转变为非负数
            int index = 0;
            while(index < n && (nums[index] - min <= sum)){
                nums[index++] -= min;
            }
            n = index;
            record = new boolean[n + 1][k + 1][sum + 1];
            record[0][0][0] = true;
            for(int i = 1; i <= n; i++){
                for(int j = 0; j <= k; j++){
                    for(int p = 0; p <= sum; p++){
                        record[i][j][p] = record[i - 1][j][p];
                        if(!(record[i][j][p]) && j > 0 && p >= nums[i - 1]){
                            record[i][j][p] = record[i - 1][j - 1][p - nums[i - 1]];
                        }
                    }
                }
            }
            rebuild(nums, n, k, sum);
            return res;
        }
        void rebuild(int[] nums, int index, int k, int sum){
            if(k <= 0){
                res.add(new LinkedList<>(stack));
                return;
            }
            if(record[index - 1][k][sum]){
                rebuild(nums, index - 1, k, sum);
            }
            //选择当前结点,需要进行过滤
            if(sum >= nums[index - 1] && record[index - 1][k - 1][sum - nums[index - 1]]){
                stack.push(nums[index - 1] + min);
                rebuild(nums, index - 1, k - 1, sum - nums[index - 1]);
                stack.pop();
            }
        }
    }
    
  • 相关阅读:
    centos 用户指定目录访问
    centos FTP 用户指定目录禁用上级目录
    centos下SVN搭建多个库文件总汇
    listview点击checkbox,修改值
    C#转成时间格式
    nmap 查看主机上开放的端口
    xargs、管道、exec区别
    OSI七层模型,作用及其对应的协议
    linux面试题(重点)
    数据库备份还原 mysqldump
  • 原文地址:https://www.cnblogs.com/echie/p/9585312.html
Copyright © 2011-2022 走看看