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

你可能感兴趣的:(SUM)