【问题标题】:Improving the time complexity in priority queue in c++提高 C++ 中优先级队列的时间复杂度
【发布时间】:2016-07-07 14:29:10
【问题描述】:

在下面的代码中,虽然它适用于较小长度的向量,但我正在为更大的向量长度超时。

     long priceCalculate(vector < int > a, long k) {
        long price = 0;
        priority_queue<int>pq(a.begin(),a.end());
        while(--k>=0){
            int x = pq.top();
            price = price + x;
            pq.pop();
            pq.push(x-1);
        }
        return price;
    }

我有一个数字数组。我必须将最大数字添加到价格,然后将该数字减 1。再次找到最大数字,依此类推。我必须重复这个过程 k 次。 有没有比时间复杂度更低的优先队列更好的数据结构?

下面是使用向量排序的代码:

    struct mclass {
        public: bool operator()(int x, int y) {
            return (x > y);
        }
    }
    compare;
    long priceCalculate(vector < int > a, long k) {

        long price = 0;
        sort(a.begin(), a.end(), compare);
        while (--k >= 0) {
            if (a[0] > 0) {

                price = price + a[0];
                a[0] = a[0] - 1;

                sort(a.begin(), a.end(), compare);
            }
        }
        return price;
    }

但这也会在大输入长度时超时。

【问题讨论】:

  • 你到底想做什么?
  • 我不太明白你的代码在做什么。你有这堆元素,取出顶部,将其添加到price,将其递减并重新插入队列,然后重复此操作k-1 次。你知道k 在一般情况下相对于a 有多大吗?如果ka 一样大,您可以对向量进行排序并避免使用优先级队列(因为弹出 n 个元素需要 O(n log n) 时间)。
  • 用代码逻辑编辑了问题。
  • 您不需要将递减的数字重新插入队列中。这是算法的草图。弹出顶部的数字。继续递减并添加它,直到它等于新的顶部(在此之前,它显然仍然是最大的)。弹出另一个元素。继续递减并添加它两次,直到它等于新的顶部(这模拟了两个元素轮流成为最大的)。弹出第三个元素,继续递减并添加它三次。以此类推。
  • 进一步的改进可以通过观察“不断递减和增加”部分相当于计算等差数列的和,可以用封闭形式表示。

标签: c++ data-structures time-complexity priority-queue


【解决方案1】:

排序代码有两个性能问题:

  1. 您在每次迭代中都使用vector&lt;&gt;。即使您的排序算法是插入排序(在这种情况下最好),它仍然需要触及向量中的每个位置,然后才能声明 vector&lt;&gt; 已排序。

  2. 更糟糕的是,您将要处理的值排序到向量的前面,需要随后的 sort() 调用来移动几乎所有元素。

因此,您可以通过

实现巨大的加速
  1. 颠倒排序顺序,以便您只与vector&lt;&gt; 的末尾进行交互。

  2. 只排序一次,然后通过扫描到正确的位置从末尾更新vector&lt;&gt;,并在那里插入新值。

您还可以仔细查看您的算法在做什么:它只对具有恒定值的 vector&lt;&gt; 的尾部进行操作,从中删除条目,然后重新插入它们,减一,在前面它。我认为您应该能够利用这些知识显着简化您的算法,从而实现更显着的加速。最后,您可以完全从vector&lt;&gt; 中删除该尾部:它完全由它的长度和它的值来描述,并且它的所有元素都可以在一个操作中进行操作。一旦你优化了算法,你的算法应该不会花时间......

【讨论】:

    【解决方案2】:

    对于矢量解决方案,您应该能够通过避免在循环中使用sort 来获得性能。

    之后

    a[0] = a[0] - 1;
    

    您可以执行以下(伪)代码之类的操作,而不是调用sort

    tmp = 0;
    for j = 1 to end-1
    {
        if a[0] < a[j] 
            ++tmp
        else
            break
     }
     swap a[0], a[tmp]
    

    要将递减的值正确放置在已排序的向量中,即由于向量是从开始排序的,您只需要找到小于或等于递减值的第一个元素并将之前的元素与 [ 0]。这应该比必须遍历整个向量的sort 更快。

    算法示例

    // Vector after decremt
    9, 10, 9, 5, 3, 2
        ^
        tmp = 1
    
    // Vector after swap
    10, 9, 9, 5, 3, 2
    

    // Vector after decremt
    9, 10, 10, 5, 3, 2
           ^
           tmp = 2
    
    // Vector after swap
    10, 10, 9, 5, 3, 2
    

    性能

    我将我的方法与 OP 中的矢量示例进行了比较:

    k = 1000
    vector.size = 10000000
    vector filled with random numbers in range 0..9999
    compiled with g++ -O3
    
    My approach:
        real 0.83
        user 0.78
        sys 0.05
    
    OPs vector approach
        real 119.42
        user 119.42
        sys 0.04
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-13
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      • 2013-02-13
      • 1970-01-01
      相关资源
      最近更新 更多