kSUM

leetcode中有几个求sum的问题,思路基本上一样,在这里一并列出。

这几道题主要思路是在使用双指针解决2SUM的基础上,将kSUM逐步reduce到2SUM。 大致框架如下:

1) sort

2) repeatedly reduce kSUM to k-1SUM, until 2SUM

3) solve 2SUM

那么问题就变成了怎样解决2SUM。这里2SUM描述成:

      “Given an sorted array of integers, find two numbers such that they add up to a specific target number”

如前所述,解决这个问题要用两个pointer,start和end,分别从数组两边向中间扫描,

if Array[start] + Array[end] > target, end--;

if Array[start] + Array[end] < target, start++;

if Array[start] + Array[end] = target, add Array[start] and Array[end] to the result set;

有时为了去掉重复元素(如 {2,2,3,5},这里第一个2和第二个2是重复的,有时只需要考虑一个就可以),每次在比较完Array[start] + Array[end]和target的大小后,要分别加上(具体用法见3SUM的code)

while(Array[start - 1] == Array[start] && start < end) start++;

while(Array[end + 1] == Array[end] && end > start) end--;

好了,这几道题的主体思路有了,我们一道一道看。

TWO SUM: https://leetcode.com/problems/two-sum/

这道题和我们在上面描述的2SUM问题有三点不同,第一是这道题输入数组没有排序,第二是这道题需要返回index,第三是这道题不需要去重。

首先我们根据上面的思路来做,先sort。但是由于结果要返回index,所以排序之前先保存一下之前的数组,最后再回来找index。之后再双指针两边扫描。时间上需要O(nlogn), 空间上需要O(n)。代码如下

class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        vector<int> ori = numbers;
        sort(numbers.begin(), numbers.end());
        int start = 0, end = numbers.size() - 1;
        while(start < end) {
            if(numbers[start] + numbers[end] < target)
                start++;
            else if(numbers[start] + numbers[end] > target)
                end--;
            else
                break;
        }
        vector<int> res;
        for(int i = 0; i < ori.size(); i++) {
            if(ori[i] == numbers[start] || ori[i] == numbers[end])
                res.push_back(i + 1);
        }
        return res;
    }
};

不过2sum这道题还有一个方法就是用hashtable把所有过往元素都存下来,这样time complexity是O(n), space也是O(n),但由于这种方法并不适合用于解决k>=3的问题,这里不做过多的讲解,详细解释可以看leetcode官方给出的solution。代码如下

class Solution {
public:
    vector<int> twoSum(vector<int> &numbers, int target) {
        unordered_map<int, int> visited;
        vector<int> res;
        for(int i = 0; i < numbers.size(); i++) {
            if(visited.find(target - numbers[i]) == visited.end()) {
                visited.insert(make_pair(numbers[i], i));
            }
            else {
                res.push_back(visited[target - numbers[i]] + 1);
                res.push_back(i + 1);
                return res;
            }
        }
        return res;
    }
};

3SUM:https://leetcode.com/problems/3sum/

这道题就是根据之前说的框架,先sort,然后reduce,然后用2SUM处理。这里的reduce很直接了,就是依次把数组中每一个元素值的负值作为2SUM中的target,start每次从当前target元素的下一个开始即可。注意代码中的去重部分。这样子O(n^2)的时间。

class Solution {
public:
    vector<vector<int> > threeSum(vector<int> &num) {
        vector<vector<int> > res;
        sort(num.begin(), num.end());
        int i = 0;
        while(i < num.size()) {
            int target = 0 - num[i];
            int start = i + 1, end = num.size() - 1;
            while(start < end) {
                if(num[start] + num[end] < target) {
                    start++;
                    //去重
                    while(start < end && num[start - 1] == num[start])
                        start++;
                }
                else if(num[start] + num[end] > target) {
                    end--;
                    //去重
                    while(start < end && num[end + 1] == num[end])
                        end--;
                }
                else {
                    vector<int> re;
                    re.push_back(num[i]);
                    re.push_back(num[start]);
                    re.push_back(num[end]);
                    res.push_back(re);
                    start++;
                    end--;
                    //去重
                    while(start < end && num[start - 1] == num[start])
                        start++;
                    while(start < end && num[end + 1] == num[end])
                        end--;
                }
            }
            i++;
            //去重
            while(i < num.size() && num[i - 1] == num[i])
                i++;
        }
        return res;
    }
};        

3SUM Closest: https://leetcode.com/problems/3sum-closest/

这道题和上一道思路几乎一样,区别是返回的要求不同而已。时间也是O(n^2)。

class Solution {
public:
    int threeSumClosest(vector<int> &num, int target) {
        sort(num.begin(), num.end());
        int close = num[0] + num[1] + num[2];
        int i = 0;
        while(i < num.size()) {
            int start = i + 1, end = num.size() - 1;
            while(start < end) {
                int sum = num[start] + num[end] + num[i];
                close = abs(close - target) < abs(sum - target) ? close : sum;
                if(sum < target) {
                    start++;
                    while(start < end && num[start - 1] == num[start])
                        start++;
                }
                else if(sum > target) {
                    end--;
                    while(start < end && num[end + 1] == num[end])
                        end--;
                }
                else
                    return close;
            }
            i++;
            while(i < num.size() && num[i - 1] == num[i])
                i++;
        }
        return close;
    }
};

4SUM:https://leetcode.com/problems/4sum/

这个系列的最后一道题,但是思路并没有变化,只是需要从4SUM reduce to 2SUM。

class Solution {
public:
    vector<vector<int> > fourSum(vector<int> &num, int target) {
        sort(num.begin(), num.end());
        vector<vector<int> > res;
        for(int i = 0; i < num.size(); i++) {
            for(int j = i + 1; j < num.size(); j++) {
                int start = j + 1, end = num.size() - 1;
                while(start < end) {
                    int sum = num[i] + num[j] + num[start] + num[end];
                    if(sum < target) {
                        start++;
                        while(start < end && num[start - 1] == num[start])
                            start++;
                    }
                    else if(sum > target) {
                        end--;
                        while(start < end && num[end + 1] == num[end])
                            end--;
                    }
                    else {
                        vector<int> re(4,0);
                        re[0] = num[i];
                        re[1] = num[j];
                        re[2] = num[start];
                        re[3] = num[end];
                        res.push_back(re);
                        start++;
                        end--;
                        while(start < end && num[start - 1] == num[start])
                            start++;
                        while(start < end && num[end + 1] == num[end])
                            end--;
                    }
                }
                while(j + 1 < num.size() && num[j] == num[j + 1])
                    j++;
            }
            while(i + 1 < num.size() && num[i] == num[i + 1])
                i++;
        }
        return res;
    }
};

如果kSUM的话,需要使用backtracking。这个问题本身是NP-hard的。

更正:

之前说的基本思路的确是可行的,而且对于kSUM=target, kSUM<target, kSUM>target (比如3SUM closest) 都适用。但是2SUM的hashtable方法在k是偶数的时候可以提高效率,只是需要更多的空间去存储。参考http://westpavilion.blogspot.com/2014/02/k-sum-problem.html的分析。

reference

http://cs.stackexchange.com/questions/2973/generalised-3sum-k-sum-problem

http://blog.csdn.net/linhuanmars/article/details/19711651

http://westpavilion.blogspot.com/2014/02/k-sum-problem.html

时间: 2024-10-22 05:13:46

kSUM的相关文章

kSum问题总结

1.2Sum 题目: 方法一:两次迭代 public class TwoSum { public static int[] twoSum(int[] nums, int target) { int[] indices = {-1,-1}; for(int i=0; i<nums.length-1; i++ ){ if(target>=nums[i]){ for(int k=i+1; k<=nums.length-1; k++){ if(nums[k] == (target-nums[i]

[算法]K-SUM problem

一.Two Sum Given an array of integers, find two numbers such that they add up to a specific target number. The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please

【数组】kSum问题

一.2Sum 思路1: 首先对数组排序.不过由于最后返回两个数字的索引,所以需要事先对数据进行备份.然后采用2个指针l和r,分别从左端和右端向中间运动:当l和r位置的两个数字之和小于目标数字target时,r减1:当l和r位置的两个数字之和大于目标数字target时,l加1.因此只需扫描一遍数组就可以检索出两个数字了.最后再扫描一遍原数组,获取这两个数字的索引. 思路2: 将数组的数组映射到哈希表,key是元素的值,value是该值在数组中的索引.考虑到数组中元素有重复,我们使用STL中的uno

2Sum,3Sum,4Sum,kSum,3Sum Closest系列

1).2sum 1.题意:找出数组中和为target的所有数对 2.思路:排序数组,然后用两个指针i.j,一前一后,计算两个指针所指内容的和与target的关系,如果小于target,i右移,如果大于,j左移,否则为其中一个解 3.时间复杂度:O(nlgn)+O(n) 4.空间:O(1) 5.代码: void twoSum(vector<int>& nums,int numsSize,int target,vector<vector<int>>& two

kSum问题的总结

kSum问题是一类题型,常见的有2Sum,3Sum,4Sum等.这篇博文就来总结一些kSum问题的解法,以及对应的时间复杂度. 1. 2Sum 在一个给定的数组nums中,寻找和为定值target的两个数. [解法1]:把数组排序,然后使用two pointers的方法来求解.时间复杂度分析:排序O(nlogn),两指针遍历O(n),总体O(nlogn). [解法2]:先遍历一遍,把整个数组存入到hash_map中,key是每个数,value是出现的次数.然后再遍历一次,每次取查找hasp_ma

[LeetCode]30. KSum问题总结

[LeetCode]1. 2Sum题目:https://leetcode.com/problems/two-sum/,解答:http://www.cnblogs.com/aprilcheny/p/4823576.html: [LeetCode]2. 3Sum题目:https://leetcode.com/problems/3sum/,解答:http://www.cnblogs.com/aprilcheny/p/4872283.html: [LeetCode]3. 3Sum Closest题目:h

【NOIP2016提高A组五校联考4】ksum

题目 分析 发现,当子段[l,r]被取了出来,那么[l-1,r].[l,r+1]一定也被取了出来. 那么,首先将[1,n]放入大顶堆,每次将堆顶的子段[l,r]取出来,因为它是堆顶,所以一定是最大的子段,输出它,并将[l+1,r]和[l,r-1]放进堆中. 一共就只用做k次就可以了. #include <cmath> #include <iostream> #include <cstdio> #include <cstdlib> #include <c

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

2015 一中培训 day 5

又是一天的爆零!!!!! 原本第一题 很容易做 竟然优化过度 丢了答案 先贴上一题 1693: ksum Time Limit 1000 ms Memory Limit 524288 KBytes Judge Standard Judge Solved 18 Submit 41 Submit Status Description Peter喜欢玩数组.NOIP这天,他从Jason手里得到了大小为n的一个正整数 数组. Peter求出了这个数组的所有子段和,并将这n(n+1)/2个数降序排序,他想