3Sum

来源:互联网 发布:java进阶书籍推荐知乎 编辑:程序博客网 时间:2024/06/15 16:34

Given an array S of n integers, are there elements a,b,c inS such thata + 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.

For example, given array S = [-1, 0, 1, 2, -1, -4],A solution set is:[  [-1, 0, 1],  [-1, -1, 2]]


解题思路:

3Sum目前见到的最好的时间复杂度是 o(n2) ,解题思路如下:

1. 先将序列进行排序,时间复杂度为 o(nlogn);

2. 在一个结果中,确定了两个数,那么第三个数也就唯一确定了;

3. 从头开始扫描序列,以当前指针(pos)指向的数字为 target,在序列中找另外两个数使得(*pos+ *left + *right  == 0);

4. 假设某一时刻,*pos+ *left + *right  == 0 成立,那么对于这个 target 就找到了一个结果;

5. 接下来 left 和 right 指针继续搜索,直到找到对于这个 target 使得等式成立的所有的 left 和 right,然后再从下一个 target 开始;

6. 因为序列中有重复数字,所以当下一个 left (或 right)指针和当前 left (或 right)指针值一样的时候,是否需要以这个 left (或 right)指针再搜索一次?答案是没必要,因为已经确定了两个数,第三个数必然一样,所以直接跳过;

    void find(int pos,vector<vector<int>> &ret,vector<int>& nums,int begin,int end)
    {
        int target = -nums[pos];
        int left = begin ;int right = end;

        while( left < right )
        {
            if(target == nums[left] + nums[right])  //找到一个 left 和 right 指针使得等式成立
            {
                vector<int> tmp;
                tmp.push_back(nums[pos]);
                tmp.push_back(nums[left]);
                tmp.push_back(nums[right]);
                ret.push_back(tmp);

                while(nums[right] == nums[right-1]) right--;// 跳过和当前 left 值一样的 left 指针,继续寻找其它满足等式的指针
                while(nums[left] == nums[left+1]) left++; // right 指针同理
            }
            if(target < nums[left] + nums[right])right--; // 根据数列有序的特点,没找到 left 和 right 的时候移动相应的指针
            else left++;
        }
    }

7. 因为序列中有重复数字,所以当下一个数字和当前 target 值一样时,是否应该再以这个 target 搜索一次?答案是没必要,因为当一个 target 搜索完成后,序列中所有满足等式的指针都已被找到,所以直接跳过;

        while(i < nums.size()-2)
        {
            find(i,ret,nums,i+1,nums.size()-1);//搜索完一个 target ( i 就是 pos 指针)
            while(i < nums.size()-2 && nums[i] == nums[i+1]) i++;//直接跳过和当前 target 值一样的 target
            i++;
        }

8. 对于新的 target , 此时 left 指针要从头开始扫描序列么?如果从头开始扫描,假设在新的 target 之前找到了一个新的 left 使得等式成立,那么说明之前这个新的 left 作为 target的时候,也可以找到这个新的 target 作为它的 left 使得等式成立;或者假设在新的 target 之前没有找到一个新的 left 使得等式成立,那么说明之前这个新的 left 作为 target,找到这个新的 target 作为它的 left 指针的时候也不能使得等式成立,所以综上 left 指针没必要从头开始扫描序列,而是从新的 target 的下一个位置处开始,因为如果从头开始扫描,无论是否能够找到,其实都已经在之前的 target 的扫描过程中搜索过了;

        while(i < nums.size()-2)
        {
            find(i,ret,nums,i+1,nums.size()-1);//搜索完一个 target ( i 就是 pos 指针) // left 指针从target 的下一个位置开始
            while(i < nums.size()-2 && nums[i] == nums[i+1]) i++;//直接跳过和当前 target 值一样的 target
            i++;
        }


完整代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <stack>

using namespace std;

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
    
        vector<vector<int>> ret;
        if(nums.size() == 0 || nums.size() == 1 || nums.size() == 2)
            return ret;
        
        sort(nums.begin(),nums.end());

        int i = 0;
        while(i < nums.size()-2)
        {
            find(i,ret,nums,i+1,nums.size()-1);
            while(i < nums.size()-2 && nums[i] == nums[i+1]) i++;
            i++;
        }
        return ret;
    }
private:
    void find(int pos,vector<vector<int>> &ret,vector<int>& nums,int begin,int end)
    {
        int target = -nums[pos];
        int left = begin ;int right = end;

        while( left < right )
        {
            if(target == nums[left] + nums[right])
            {
                vector<int> tmp;
                tmp.push_back(nums[pos]);
                tmp.push_back(nums[left]);
                tmp.push_back(nums[right]);
                ret.push_back(tmp);

                while(nums[right] == nums[right-1]) right--;
                while(nums[left] == nums[left+1]) left++;
            }
            if(target < nums[left] + nums[right])right--;
            else left++;
        }
    }

};

void main()
{
    vector<int> nums ;
    int tmp;
    while(cin >> tmp)nums.push_back(tmp);
    Solution s;
    s.threeSum(nums);
}

原创粉丝点击