時間復雜度:
堆排序 歸並排序 快速排序
最壞時間 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
