现在的位置: 首页 > 综合 > 正文

LeetCode_3Sum

2019年10月08日 ⁄ 综合 ⁄ 共 2175字 ⁄ 字号 评论关闭

一.题目

3Sum

  Total Accepted: 45112 Total
Submissions: 267165
My
Submissions

Given an array S of n integers, are there elements abc in S such that a + b + c =
0? Find all unique triplets in the array which gives the sum of zero.

Note:

  • Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
  • The solution set must not contain duplicate triplets.
    For example, given array S = {-1 0 1 2 -1 -4},

    A solution set is:
    (-1, 0, 1)
    (-1, -1, 2)
Show Tags
Have you met this question in a real interview?  

Yes

 

No

Discuss













二.解题技巧

    这道题和另外一道题Two Sum很类似,不过这道题是在数组中寻找三个数,使得其和为0,同时要求这三个数只能出现一次。如果单纯得使用暴力算法来做的话,时间复杂度为O(n^3),且很难判断这一组数是否已经出现过。
    如果考虑将数组A(元素个数为n)进行升序排序,那么按照顺序将数组中的第i个数作为三个数中最小的数,寻找从A的第i+1个数到n-1个数中满足和为-A[i]的数,就可以找到满足三个数和为0的组合了,但是,单独考虑这种情况,会出现重复的问题。要保证不出现重复的情况,当i!=0时,如果第i个数与第i-1个数相同的话,则不进行处理,直接处理第i+1个元素。这样,只要保证三个数中最小的数是按照顺序递增的,那么算法找到的解就都是不重复的。
    这道题的边界条件在于:由于我们选择的是三个数中的最小值,因此,对于这个数的循环是[0, n-2),同时,要对最小值进行消除重复的元素时,需要从第1个元素开始判断,如果从第0个元素开始判断的时候,可能会将0,0,0这种情况忽略掉,因此,在消除重复的最小元素时,必须从第1个元素才开始。
    这道题在寻找数组A中的两个数的和为-A[i]时,可以考虑利用数组A是已经排序的条件,进行左右夹逼操作。在进行左右搜索的时候,需要将左右两边收缩到与当前元素不同的元素为止,这样做有两个原因:1.可以减少计算量;2.当当前的两个元素的和刚好等于-A[i]的时候,如果没有进行上面的缩放操作,那么就可能将重复的三个数保存下来,导致结果出错。


三.实现代码

class Solution
{
public:
    vector<vector<int> > threeSum(vector<int> &num)
    {
        int Size = num.size();

        vector<vector<int> > Result;

        if (Size < 3)
        {
            return Result;
        }

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

        for (int Index_outter = 0; Index_outter < (Size - 2); Index_outter++)
        {
            int First = num[Index_outter];
            int Second = num[Index_outter + 1];
            const int Target = 0;

            if ((Index_outter != 0) && (First == num[Index_outter - 1]))
            {
                continue;
            }

            int Start = Index_outter + 1;
            int End = Size - 1;

            while (Start < End)
            {
                Second = num[Start];
                int Third = num[End];

                int Sum = First + Second + Third;

                if (Sum == Target)
                {
                    vector<int> Tmp;
                    Tmp.push_back(First);
                    Tmp.push_back(Second);
                    Tmp.push_back(Third);

                    Result.push_back(Tmp);

                    Start++;
                    End--;
                    while (num[Start] == num[Start - 1])
                    {
                        Start++;
                    }
                    while (num[End] == num[End + 1])
                    {
                        End--;
                    }


                }

                if (Sum < Target)
                {
                    Start++;
                    while (num[Start] == num[Start -1])
                    {
                        Start++;
                    }
                }

                if (Sum > Target)
                {
                    End--;
                    if (num[End] == num[End + 1])
                    {
                        End--;
                    }
                }

            }

        }
    return Result;

    }
};


四.体会

    我自己的想法时,将数组进行排序,然后按照顺序选择数组A[1, n-1)中的元素作为三个数中的中位数来在剩下的已经排好序的数组中寻找满足条件的其他两个数。但是,选择中位数的这种情况需要考虑的边界条件比较复杂,并没有选择最小值来得方便一些,同时,选择中位数之后,将已经排序的数组分成了两段,这样在进行收缩的时候,需要分别判断两个两边与i的关系,也比较容易出错。
    这道题通过对数组进行排序,从而按照顺序选择不同的最小值来避免出现重复的情况,这确实是一个比较好的编程技巧。



版权所有,欢迎转载,转载请注明出处,谢谢微笑




抱歉!评论已关闭.