Leetcode 15 / LintCode 57: 3Sum

这题其实不容易调通,难就难在对重复元素的排除上。比如说a[]={-2,0,0,2,2},只能输出一个[-2,0,2],但对于a[]={-4,-1,-1,0,1,2,3},必须要输出2个: [-1,-1,2]和[-1,0,1],而对于a[]={0,0,0,0},又只能输出一个[0,0,0]。

注意:

1) 此题不存在O(nlogn)的解法。我一开始设想了一个解法是用两个指针p1,p2,一前一后。假设两个指针对应数和为sum,中间那个数用binary search找-sum。如果找不到,并且sum>0,就p2--,否则p1++。

比如说,a[]={-7,-4,0,1,1,3},-7+3=-4找4找不到,p1++,-4+3=-1,找到了。

但这种方法是不对的。举个反例,a[]={-7,-4,-4,0,1,1,2,8},-7+8=1,此时要p2--,但p2不能动,因为-4-4+8=0。这个binary search的方法不对的原因我认为是:如果找不到解,不能确定p1和p2怎么动,有可能只p1++,也可能只p2--,也可能p1++和p2--都要进行,所以这种试图两边往中间移动的思路不对。

2) 正解的方法是只锁定一边,也就是i++,然后从i+1到nums.size()-1里面用p1,p2,先算sum2=nums[p1]+nums[p2],然后sum2和-nums[i]比较,若sum2>-nums[i],则p2--; 若sum2<-nums[i],则p1++;如果两者相等,说明找到一个解。要注意此时应该再加一个while循环,把所有的解都弄出来。

这两行非常重要,里面又是两个小循环:

       while ((++p1

它们表示将p1,p2移到不重复的地方,假设nums[1]=-1, p1~p2对应[-1,-1,0,1,2],此时找到一个解[-1,-1,2]。第一个while会将数组变成[-1,0,1,2],第二个while会将数组变成[-1,0,1],然后while(p1

但如果input数组是[-2,0,0,2,2,2,2]时,一开始while(p1

我一开始将上面两个循环写成

       while ((p1
这是不对的,因为第2个循环的(nums[p2]==nums[p2+1])不满足,所以p2不会--。也就是说,此时应该先无条件p1++,p2--,然后再看p1和p2的相邻元素是否一致。


3) 最开始的 if ((i==0)||(nums[i]!=nums[i-1]))也可以防止重复解出现。如果nums[i-1]和nums[i]都是解的一部分也没关系,上次i-1的时候就已经输出了(因为已经i++),这是可以直接跳过nums[i]。

4) 此题避免重复解也可以用set或map。

#include 
#include 
#include 

using namespace std;

vector > threeSum(vector& nums) {
    int i=0, p1=0, p2=0;
    vector > sol;

    if (nums.size()==0) return sol;

    sort(nums.begin(), nums.end());

    while(i-nums[i])
                p2--;
            else if (sum2<-nums[i])
                p1++;
            else {
                //find one solution
                vector tmpV;
                tmpV.push_back(nums[p1]); tmpV.push_back(nums[p2]); tmpV.push_back(nums[i]);
                sol.push_back(tmpV);
                cout<<" ["< S1={-1, 0, 1, 2, -1, -4};
    threeSum(S1);
    cout< S2={0,0,0,0};
    threeSum(S2);
    cout< S3={-2,0,1,1,2};
    threeSum(S3);
    cout< S4={-2,0,0,2,2};
    threeSum(S4);
    cout< S5={-2,0,1,1,1,2};
    threeSum(S5);
    cout< S6={-3,1,2,2,2,2};
    threeSum(S6);
    cout< S7;
    threeSum(S7);
    cout<

又做了一遍,第二次的代码如下,其实跟第一次差不多。

    vector> threeSum(vector &numbers) {
        vector> result;
        if (numbers.size()<3) return result;
        int p1=0, p2=0, p3=0;
        sort(numbers.begin(), numbers.end());
        for (p3=2; p3 temp={numbers[p1],numbers[p2],numbers[p3]};
                    result.push_back(temp);
                    while(p1

 

3刷,发现自己以前水平太次了。

class Solution {
public:
    /**
     * @param numbers: Give an array numbers of n integer
     * @return: Find all unique triplets in the array which gives the sum of zero.
     */
    vector> threeSum(vector &numbers) {
        int n = numbers.size();
        if (n < 3) return {{}};
        sort(numbers.begin(), numbers.end());
        int p1 = 0, p2 = 0, p3 = 2;
        set> s;
        vector> result;
        while(p3 < n) {
            p1 = 0; p2 = p3 - 1;
            int target = -numbers[p3];
            while(p1 < p2) {
                int sum = numbers[p1] + numbers[p2];
                if (sum == target) {
                    s.insert({numbers[p1], numbers[p2], numbers[p3]});
                    p1++; p2--;
                } else if (sum > target) {
                    p2--;
                } else {
                    p1++;
                }
            }
            p3++;
        }
        
        result.assign(s.begin(), s.end());
        return result;
    }
};

 

 

代码同步在
https://github.com/luqian2017/Algorithm

你可能感兴趣的:(Leetcode 15 / LintCode 57: 3Sum)