堆排序CPU时间

Heapsort CPU time

本文关键字:时间 CPU 堆排序      更新时间:2023-10-16

我在c++中实现了Heapsort,它确实对数组进行了排序,但给了我比预期更高的CPU时间。它应该花费nlog(n)个flops,并且它应该至少比bubblesort和insertitionsort更快地对其进行排序。

相反,它给了我比bubblesort和insertion-sort更高的cpu时间。例如,对于int的随机数组(大小100000),我有以下cpu时间(以纳秒为单位):

  • 气泡排序:1.0957e+11
  • 插入排序:4.46416e+10
  • 合并排序:7.2381e+08
  • 堆排序:2.04685e+11

这就是代码本身:

   #include <iostream>
    #include <assert.h>
    #include <fstream>
    #include <vector>
    #include <random>
    #include <chrono>
    using namespace std;
    typedef vector<int> intv;
    typedef vector<float> flov;
    typedef vector<double> douv;

        void max_heapify(intv& , int);
        void build_max_heap(intv& v);
        double hesorti(intv& v)
        {
            auto t0 =chrono::high_resolution_clock::now();
            build_max_heap(v);
            int x = 0;
            int i = v.size() - 1;
            while( i > x)
            {
                swap(v[i],v[x]);
                ++x;
                --i;
            }
            auto t1 = chrono::high_resolution_clock::now();
            double T = chrono::duration_cast<chrono::nanoseconds>(t1-t0).count();
            return T;
        }
        void max_heapify(intv& v, int i)
        {
            int left = i + 1, right = i + 2;
            int largest;
            if( left <= v.size() && v[left] > v[i])
            {
                largest = left;
            }
            else
            {
                largest = i;
            }
            if( right <= v.size() && v[right] > v[largest])
            {
                largest = right;
            }
            if( largest != i)
            {
                swap(v[i], v[largest]);
                max_heapify(v,largest);
            }

        }
        void build_max_heap(intv& v)
        {
            for( int i = v.size() - 2; i >= 0; --i)
            {
                max_heapify(v, i);
            }
        }

堆排序的实现肯定有问题。

查看hesorti,可以看到它只是在调用build_max_heap之后反转向量的元素。所以build_max_heap不只是堆,它实际上是对整个数组进行反向排序。

max_heapify已经存在一个问题:在堆的标准数组布局中,数组索引i处的节点的子级不是i+1i+2,而是2i+1和<2i+2。它是从数组的后面从build_max_heap向前调用的。这是干什么的?

第一次调用它时,在最后两个元素上(当i=n-2时),它只是确保越大越好。之后调用时会发生什么?

让我们做一些数学归纳。假设,对于所有j>i,在一个数组上调用索引为jmax_heapify之后,其中数字v[j+1]v[n-1]已经按降序排列,结果是数字v[j][n-1][em>按降序排列。(我们已经看到,当i=n-2时,这是真的。)

如果v[i]大于或等于v[i+1][因此,v[i+2]n-1处的值按降序排列。在另一种情况下会发生什么?

这里,largest被设置为i+1,并且根据我们的假设,v[i+1]大于或等于v[i+2][事实上,对于k>i+1的所有v[k]+2)的测试永远不会成功v[i][em>与v[i+1][em>交换,使v[i][em>成为从v[i][im>到v[n-1][em>的最大数,然后对从i+1到末尾的元素调用max_heapify。根据我们的归纳假设,这将按降序对这些元素进行排序,因此我们知道,现在从v[i]v[n-1]

通过归纳的力量,我们证明了build_max_heap会对元素进行反向排序。它的方法是从后面依次过滤元素,使其在后面的反向排序元素中处于正确的位置

这看起来熟悉吗?这是插入排序!除了它是反向排序之外,所以当调用hesorti时,交换序列会将其按正确的顺序排列。

插入排序也有O(n^2)的平均行为,这就是为什么你会得到与冒泡排序相似的数字。由于插入步骤的复杂实现,它几乎肯定会更慢。

TL;DR:你的堆排序并不快,因为它实际上不是一个堆排序,它是一个向后插入排序,然后是一个原位排序反转。