C ++ 3sum的复杂性

时间:2017-07-14 00:56:45

标签: c++ time-complexity

我试图解决cpp中的3和问题。

给定n个整数的数组S,S中是否有元素a,b,c,a + b + c = 0?找到数组中所有唯一的三元组,它们总和为零。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int size = nums.size();
        vector<vector<int>> result;
        for (int i = 0; i < size - 2; ++i) {
            for (int j = i + 1; j < size - 1; ++j) {
                for (int k = j + 1; k < size; ++k) {
                    if (sumToZero(i, j, k, nums)) {
                        vector<int> newComb = vectorify(i, j, k, nums);
                        //printComb(newComb);
                        if (!exist(newComb, result)) {
                            //cout << "not exist" << endl;
                            result.push_back(newComb);
                        } else {
                            //cout << "exist" << endl;
                        }
                    }
                }
            }
        }
        return result;
    }

    bool sumToZero(int i, int j, int k, vector<int>& nums) {
        return nums[i] + nums[j] + nums[k] == 0;
    }

    vector<int> vectorify(int i, int j, int k, vector<int>& nums) {
        vector<int> result;
        result.push_back(nums[i]);
        result.push_back(nums[j]);
        result.push_back(nums[k]);
        return result;
    }

    void printComb(vector<int>& input) {
        cout << input[0] << input[1] << input[2] << endl;
    }

    bool isSameComb(vector<int>& a, vector<int> b) {
        for (int i = 0; i < b.size(); ++i) {
            if (a[0] == b[i]) {
                b.erase(b.begin() + i);
            }
        }
        for (int i = 0; i < b.size(); ++i) {
            if (a[1] == b[i]) {
                b.erase(b.begin() + i);
            }
        }
        for (int i = 0; i < b.size(); ++i) {
            if (a[2] == b[i]) {
                b.erase(b.begin() + i);
            }
        }
        return b.empty();
    }

    bool exist(vector<int>& niddle, vector<vector<int>>& haystack) {
        int size = haystack.size();
        for (int i = 0; i < size; ++i) {
            if (isSameComb(niddle, haystack[i])) {
                return true;
            }
        }
        return false;
    }
};

但是,此解决方案超出了时间限制。我想不出额外复杂性的来源。有人可以帮我指出我在哪里做额外的计算吗?

4 个答案:

答案 0 :(得分:3)

额外复杂性的来源是第三个循环,它将代码的时间复杂性带到O(n 3 )。

这里的关键观察是,一旦你有两个数字,第三个数字是固定的,所以你不需要循环找到它:使用哈希表来查看它是否存在于O(1 )。例如,如果您的第一个循环查看值为56,而您的第二个循环查看值-96,则第三个值必须为40才能产生零总数。

如果数字范围相当小(例如,-10000..10000),则可以使用数组。

这会给O(n 2 )带来时间复杂度,这应该是对时间的明显改善。

答案 1 :(得分:2)

您可以在O(n²)中使用以下内容:

std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
    std::sort(nums.begin(), nums.end());

    std::vector<std::vector<int>> res;
    for (auto it = nums.begin(); it != nums.end(); ++it) {
        auto left = it + 1;
        auto right = nums.rbegin();
        while (left < right.base()) {
            auto sum = *it + *left + *right;
            if (sum < 0) {
                ++left;   
            } else if (sum > 0) {
                ++right;   
            } else {
                res.push_back({*it, *left, *right});
                std::cout << *it << " " <<  *left << " " << *right << std::endl;
                ++left;
                ++right;
            }
        }
    }
    return res;
}

Demo

我让重复处理作为练习。

答案 2 :(得分:0)

有几种可能性:

首先,在前面的向量中构造所有条目的哈希表,然后删除第三个循环。在第二个循环中,只需检查哈希表中是否存在-nums[i] - nums[j]。这应该会使您的时间复杂度从O(n3)回到更接近O(n2)的位置。

其次,函数调用不是免费的,尽管优化器有时可以大大提高。没有性能的原因你应该调用一个函数来检查三个数字是否加零以便你可以替换:

if (sumToZero(i, j, k, nums)) {

使用:

if (nums[i] + nums[j] == -nums[k]) {

当然,如果你采纳第一个建议,这就没有实际意义了。

第三,每次获得结果时都不要检查并插入可能的结果。无论如何,只需将其添加到矢量中即可。然后,最后,对矢量进行排序并删除任何重复项。这应该有希望加快速度。

第四,当int[3]同样可以使用向量作为潜在结果时,很可能会有性能损失。如果你需要一个可变大小的东西,矢量是理想的,但是,如果数组类型集合的最小和最大大小总是固定值,原始数组就可以了。

但也许最重要的建议是衡量,不要猜测!

确保在每次尝试优化后,您都要测试它是否具有有害,可忽略或有益的效果。各种数据集的测试套件以及流程的自动化将使这一过程变得更加容易。但是,即使你必须手动完成,也要这样做 - 你无法改进你无法衡量的东西。

答案 3 :(得分:0)

这是我的解决方案,可以在O(n ^ 2)运行时查找所有唯一个三元组。

class Solution {

    public: vector<vector<int>> threeSum(vector<int>& nums) {    
        int len = nums.size();

        if(len<3) return {};

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

        vector<vector<int>> retVector;

        int target, begin, end;    

        int i=0;
        while(i < len - 2)                  
        {
            int dup;    //  to find duplicates entries

            target = -nums[i];

            begin = i + 1; end = len - 1;

            while (begin < end)
                {
                    if (nums[begin] + nums[end] < target) begin++;                  

                    else if (nums[begin] + nums[end] > target) end--;                   

                        else
                        {

                            retVector.push_back({nums[i], nums[begin], nums[end]}); 

                            //  its time to remove duplicates

                            dup=nums[begin];    

                            do begin++; while(nums[begin] == dup);   //  removing from front

                            dup=nums[end]; 

                            do end--; while(nums[end] == dup);     //  removing from back                                                        
                        }                                                                                               
                }

            dup=nums[i];

            do i++; while(nums[i] == dup) ;     //  removing all ertries same as nums[i]            

        }                                      
        return retVector;
    }     
};