【问题标题】:C++ 3sum complexityC++ 3sum 复杂度
【发布时间】:2017-07-14 00:56:44
【问题描述】:

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

给定一个包含 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;
    }
};

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

【问题讨论】:

标签: c++ time-complexity


【解决方案1】:

您可以在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】:

    几种可能性:


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


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

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

    与:

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

    当然,如果您采纳第一个建议,这将变得毫无意义。


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


    第四,当int[3] 也可以做到时,使用向量作为潜在结果很可能会影响性能。如果您需要具有可变大小的东西,那么向量是理想的选择,但是如果数组类型集合的最小和最大大小始终是固定值,那么原始数组就可以了。


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

    确保在每次尝试优化后,都进行测试以查看它是否具有有害、微不足道或有益的影响。一个包含各种数据集的测试套件,并自动化该过程,将使这变得更加容易。但是,即使您必须手动进行,也请这样做 - 您无法改进无法衡量的内容。

    【讨论】:

      【解决方案3】:

      额外复杂性的来源是第三个循环,它使您的代码的时间复杂度达到 O(n3)。

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

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

      这会将时间复杂度提高到 O(n2),这应该是对时间的显着改进。

      【讨论】:

        【解决方案4】:

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

        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;
            }     
        };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-03-06
          • 2019-12-29
          • 2013-11-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多