關於堆排序、歸並排序、快速排序的比較


時間復雜度:

            堆排序      歸並排序        快速排序
最壞時間   O(nlogn)     O(nlogn)        O(n^2)
最好時間   O(nlogn)     O(nlogn)        O(nlogn)
平均時間   O(nlogn)     O(nlogn)        O(nlogn)

輔助空間     O(1)         O(n)        O(logn)~O(n)

從時間復雜度看堆排序最好

有人說代碼實現后,數據量足夠大的時候,快速排序的時間確實是比堆排序短

解釋是,對於數組,快速排序每下一次尋址都是緊挨當前地址的,而堆排序的下一次尋址和當前地址的距離比較長。

網友解答:

1#

4種非平方級的排序:
希爾排序,堆排序,歸並排序,快速排序

我測試的平均排序時間:數據是隨機整數,時間單位是秒
數據規模    快速排序    歸並排序    希爾排序    堆排序
1000萬       0.75           1.22          1.77          3.57
5000萬       3.78           6.29          9.48         26.54  
1億             7.65          13.06        18.79        61.31

堆排序是最差的。
這是算法硬傷,沒辦法的。因為每次取一個最大值和堆底部的數據(記為X)交換,重新篩選堆,把堆頂的X調整到位,有很大可能是依舊調整到堆的底部(堆的底部X顯然是比較小的數,才會在底部),然后再次和堆頂最大值交換,再調整下來。
從上面看出,堆排序做了許多無用功。

至於快速排序為啥比歸並排序快,我說不清楚。

 

2#

算法復雜度一樣只是說明隨着數據量的增加,算法時間代價增長的趨勢相同,並不是執行的時間就一樣,這里面有很多常量參數的差別,即使是同樣的算法,不同的人寫的代碼,不同的應用場景下執行時間也可能差別很大。

快排的最壞時間雖然復雜度高,但是在統計意義上,這種數據出現的概率極小,而堆排序過程里的交換跟快排過程里的交換雖然都是常量時間,但是常量時間差很多。

 

3#

請問你的快快速排序是怎么寫的,我寫的快速排序,當測試數組大於5000的時候就棧溢出了。
其他的幾個排序都對着,不過他們呢沒有用棧。
這是快速排序的代碼,win7 32位,vs2010.

 1 int FindPos(double *p,int low,int high)
 2 {
 3     double val = p[low];
 4     while (low<high)
 5     {
 6         while(low<high&&p[high]>=val)
 7             high--;
 8         p[low]=p[high];
 9         while(low<high&&p[low]<val)
10             low++;
11         p[high]=p[low];
12     }
13     p[low]=val;
14     return low;
15 }
16 void QuickSort(double *a,int low,int high)
17 {
18     if (!a||high<low)
19         return;
20 
21     if (low<high)
22     {
23         int pos=FindPos(a,low,high);
24         QuickSort(a,low,pos-1);
25         QuickSort(a,pos+1,high);
26     }
27 }


……

7#

誰說的快排好啊?我一般都用堆的,我認為堆好。

這個哪有譜啊,考察使用環境啊。不是只有時間代價一種衡量標准的,還有空間代價,LZ也有過棧溢出的經歷,那不就是空間代價過高了嘛。改成別的辦法還是避免不了空間代價。LZ自己不是也已經把空間待見列出來了嘛。

 

……

11#

我自己按照堆排序的算法思想實現了堆排序,寫完后對照網上的代碼,代碼幾乎是差不多的。
至於為何堆排序效率比希爾排序慢很多,我在上面帖子已經說了,是堆排序的算法缺陷造成無用功太多。(我把堆排序看成是高級排序中的冒泡排序,冒泡也是兩兩交換做了太多無用功所以最慢。)
貼下我實現的希爾排序和堆排序,你可以自己去運行比較。

 1 template <typename T>
 2 void ShellSort(T arr[], int n)          //希爾排序就是增量逐步縮小的直接插入排序
 3 {                                       //這里采用黃金分割的比例逐次減小增量
 4     int gap = n * 0.382 + 0.5;          //所以代碼和直接插入排序非常像。區別就是把增量1換成gap
 5     int i, j;
 6     T temp;
 7     while (gap > 0)
 8     {
 9         for (i = gap; i < n; i++)
10         {
11             temp = arr[i];
12             for (j = i - gap; j >= 0; j -= gap)
13             {
14                 if (temp < arr[j])
15                     arr[j + gap] = arr[j];
16                 else
17                     break;
18             }
19             arr[j + gap] = temp;
20         }
21         gap = gap * 0.382 + 0.5;
22     }
23 }


 

 1 template <typename T>
 2 void HeapSort(T arr[], int n)
 3 {
 4     for (int i = n / 2 - 1; i >= 0; i--)    //建立最大堆,從最后一個非葉節點開始
 5         MaxHeapify(arr, n, i);              //對於N規模的堆(0,n-1), n/2-1是最后一個非葉節點
 6 
 7     T temp;
 8     for (int i = n - 1; i > 0; i--)   
 9     {
10         temp = arr[i];               //每次取堆頂數據和堆底部數據交換
11         arr[i] = arr[0];             //並逐步縮小堆的大小
12         arr[0] = temp;
13         MaxHeapify(arr, i, 0);       //重新篩選堆
14     }
15 }
16 
17 
18 template <typename T>
19 void MaxHeapify(T arr[], int n, int i)
20 {
21     T temp, max_data;
22     int pos = i;
23     int left = pos * 2 + 1;
24     int right = left + 1;
25     int max_pos;
26     temp = arr[pos];
27 
28     while (left < n)     //至少有左子樹
29     {
30         //取左右子樹的最大值
31         max_data = arr[left];
32         max_pos = left;
33         if (right < n)   //左右子樹均有
34             if (arr[right] > arr[left])
35             {
36                 max_data = arr[right];
37                 max_pos = right;
38             }
39 
40         if (temp < max_data)         //節點比左右子樹最大值小,繼續向下調整
41         {
42             arr[pos] = max_data;
43             pos = max_pos;
44             left = pos * 2 + 1;
45             right = left + 1;
46         }
47         else
48             break;
49     }
50     arr[pos] = temp;   //回填
51 }

 

12#

Mark一下, 有時間好好做一些實驗,給出具體結果。

這里先說說的我的看法,希望實驗能支持我的預測。
我認為,
1. 快排的時間復雜度是不穩定的,在最快情況下比歸並排序慢的多。
2. 當數據量大時,充分優化的歸並排序可比快速排序更快。其原因有
  1). 歸並排序對內存的訪問是嚴格的順序方式(3個2個源數組,1個目標數組,都是順序放分),故cache的命中率比快排更高,從這點上,相同的內存讀寫操作,歸並優於快排,當數組占用的空間大大超過cache的大小,則這一優勢將更加明顯。
  2)普通寫法的歸並排序有2個缺點,如果改進,則可以提速。如果你的實驗是基於最普通的版本,得到的結果是快排優於歸並,而優化的歸並排序的版本,其性能則可能反超快排。
  2.1) 歸並排序不是In place.需要將結果存儲到臨時緩沖區,然后在復制回來,這個過程可以優化掉。使用乒乓做法,在第i級歸並過程,從buff1 歸並到buff2,在i+1級歸並過程,從buff2復制到buff1。
  2.2) 2路歸並排序的核心動作是比較2個對列的頭元素那個更大,其比較結果是隨機的,2個分支機會均等,CPU的分支預測算法不起作用,當預測失敗,可大大降低程序性能,如果消除這個分支,可明顯提高程序性能。

 

13#

樓主知道標准庫的sort是怎么實現的么? 先快排,遞歸深度超過一個閥值就改成堆排,然后對最后的幾個進行插入排序。    //233系統真機智

 

14#

1.快排的時間復雜度確實不穩定,極端情況是O(n^2),但是平攤下來是T(n*lg(n)),而歸並是嚴格的O(n*log(n))。
2.快速排序比歸並排序快。其原因有
  1)快排對內存的訪問是順序方式(包括逆序),只有兩個目標而且是同一個數組,故cache的命中率不會比歸並低。特別是數組空間接近於cache大小時,這一優勢將更加明顯。
  2)快排的內存寫操作次數平攤下來是T(n*lg(n)/2),而歸並的內存寫操作次數是嚴格的O(n*log(n)),由於內存寫操作開銷比較大,所以對於隨機數據快排優於歸並。

 

……

16#

同學們,這些算法都有標准的開源程序。
自己寫程序對理解算法的確有好處。
但是如果用於得到一般性的比較結果,那是靠不住的。

 

原帖到此終結;

 

文自:http://bbs.csdn.net/topics/390554511

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM