各類排序算法時間複雜度、穩定性、初始序列是否對元素比較次數有關

怎麼記憶穩定性算法

總過四大類排序:插入、選擇、交換、歸併(基數排序暫且不算)shell

比較高級一點的(時間複雜度低一點得)shell排序,堆排序,快速排序(除了歸併排序)都是不穩定的,在加上低一級的選擇排序是不穩定的。數組

比較低級一點的(時間複雜度高一點的)插入排序,               冒泡排序,歸併排序,基數排序都是穩定的。數據結構

(4種不穩定,4種穩定)。性能

怎麼記憶初始序列是否對元素的比較次數有關:優化

[cpp] view plaincopyui

  1. /**
  2.   * @brief 嚴版數據結構書代碼
  3.   *        最好的狀況,數組自己有序,就只需執行n-1次比較,此時時間複雜度爲O(n);
  4.   *        最壞的狀況,數組自己逆序,要執行n(n-1)/2次,此時時間複雜度爲O(n^2);
  5.   */
  6. void _insertSort(int R[], int n) 
  7. int i, j, temp; 
  8. for ( i = 1; i < n; ++i ) { 
  9. if ( R[i] < R[i - 1] ) {//將R[i]插入有序字表
  10.             temp = R[i];        //設置哨兵
  11. for ( j = i - 1; R[j] > temp; --j ) { 
  12.                 R[j+1] = R[j]; 
  13.             } 
  14.             R[j+1] = temp; 
  15.         } 
  16.     } 

對於直接插入排序:.net

當最好的狀況,若是原來自己就是有序的,比較次數爲n-1次(分析(while (j >= 0 && temp < R[j]))這條語句),時間複雜度爲O(n)。blog

當最壞的狀況,原來爲逆序,比較次數爲2+3+...+n=(n+2)(n-1)/2次,而記錄的移動次數爲i+1(i=1,2...n)=(n+4)(n-1)/2次。排序

若是序列是隨機的,根據機率相同的原則,平均比較和移動的次數爲n^2/4.

[cpp] view plaincopy

  1. /**
  2.   * @brief 嚴版數據結構 選擇排序
  3.   *        採用"選擇排序"對長度爲n的數組進行排序,時間複雜度最好,最壞都是O(n^2)
  4.   *        當最好的時候,交換次數爲0次,比較次數爲n(n-1)/2
  5.   *        最差的時候,也就初始降序時,交換次數爲n-1次,最終的排序時間是比較與交換的次數總和,
  6.   *        總的時間複雜度依然爲O(n^2)
  7.   */
  8. void _selectSort(int R[], int n) 
  9. int i, j, temp, index; 
  10. for ( i = 0; i < n; ++i ) { 
  11.         index = i; 
  12. for ( j = i + 1; j < n; ++j ) { 
  13. if ( R[index] > R[j] ) { 
  14.                 index = j;//index中存放關鍵碼最小記錄的下標
  15.             } 
  16.         } 
  17. if (index != i) { 
  18.             temp = R[i]; 
  19.             R[i] = R[index]; 
  20.             R[index] = temp; 
  21.         } 
  22.     } 

選擇排序不關心表的初始次序,它的最壞狀況的排序時間與其最佳狀況沒多少區別,其比較次數都爲 n(n-1)/2,交換次數最好的時候爲0,最差的時候爲n-1,儘管和冒泡排序同爲O(n),但簡單選擇排序性能上要優於冒泡排序。但選擇排序能夠   很是有效的移動元素。所以對次序近乎正確的表,選擇排序可能比插入排序慢不少。

[cpp] view plaincopy

  1. /**
  2.   * @brief     改進的冒泡排序
  3.   * @attention 時間複雜度,最好的狀況,要排序的表自己有序,比較次數n-1,沒有數據交換,時間複雜度O(n)。
  4.   *            最壞的狀況,要排序的表自己逆序,須要比較n(n-1)/2次,並作等數量級的記錄移動,總時間複雜度爲O(n^2).
  5.   */
  6. void bubbleSort2(int R[], int n) 
  7. int i, j, temp; 
  8. bool flag = TRUE;   //flag用來做爲標記
  9. for ( i = 0; i < n && flag; ++i ) { 
  10.         flag = FALSE; 
  11. for ( j = n - 1; j > i; --j ) { 
  12. if (R[j] < R[j - 1]) { 
  13.                 temp = R[j]; 
  14.                 R[j] = R[j - 1]; 
  15.                 R[j - 1] = temp; 
  16.                 flag = TRUE;//若是有數據交換,則flag爲true
  17.             } 
  18.         } 
  19.     } 

冒泡排序:

最好的狀況,n-1次比較,移動次數爲0,時間複雜度爲O(n)。

最壞的狀況,n(n-1)/2次比較,等數量級的移動,時間複雜度爲O(O^2)。

[cpp] view plaincopy

  1. /**
  2.   * @brief 希爾排序, 對於長度爲n的數組,通過 "希爾排序" 輸出
  3.   */
  4. void shellSort(int R[], int n) 
  5. int i, j, temp; 
  6. int k = n / 2; 
  7. while (k >= 1) { 
  8. for (i = k; i < n; ++i) { 
  9.             temp = R[i]; 
  10.             j = i - k; 
  11. while (R[j] < temp && j >= 0) { 
  12.                 R[j+k] = R[j]; 
  13.                 j = j - k; 
  14.             } 
  15.             R[j+k] = temp; 
  16.         } 
  17.         k = k / 2; 
  18.     } 

希爾排序初始序列對元素的比較次數有關。

[cpp] view plaincopy

  1. /**
  2.   * @brief     構建 大頂堆
  3.   * @attention 我的版本,堆排序
  4.   */
  5. void heapAdjust(int R[], int start, int end) 
  6. int j, temp; 
  7.     temp = R[start]; 
  8. for ( j = 2 * start + 1; j <= end; j = j * 2 + 1 ) { 
  9. if ( j < end && R[j] < R[j + 1] ) { 
  10.             ++j; 
  11.         } 
  12. if ( temp >  R[j] ) { 
  13. break; 
  14.         } 
  15.         R[start] = R[j]; 
  16.         start = j; 
  17.     } 
  18.     R[start] = temp; 
  19. /**
  20.   * @brief 堆排序
  21.   * @param R爲待排序的數組,size爲數組的長度
  22.   *  時間複雜度:構建大(小)頂堆,徹底二叉樹的高度爲log(n+1),所以對每一個結點調整的時間複雜度爲O(logn)
  23.   *           兩個循環,第一個循環作的操做次數爲n/2,第二個操做次數爲(n-1),所以時間複雜度爲O(nlogn)
  24.   */
  25. void heapSort(int R[], int size) 
  26. int i, temp; 
  27. for ( i = size / 2 - 1; i >= 0; --i ) { 
  28.         heapAdjust(R, i, size); 
  29.     } 
  30. for ( i = size - 1; i >= 0; --i ) { 
  31.         temp = R[i]; 
  32.         R[i] = R[0]; 
  33.         R[0] = temp;//表尾和表首的元素交換
  34.         heapAdjust(R, 0, i - 1);//把表首的元素換成表尾的元素後,從新構成大頂堆,由於除表首的元素外,
  35. //後面的結點都知足大頂堆的條件,故heapAdjust()的第二個參數只需爲0
  36.     } 

[cpp] view plaincopy

  1. /**
  2.   * @brief 將有序的長度爲n的數組a[]和長度爲m的b[]歸併爲有序的數組c[]
  3.   *        只要從比較二個數列的第一個數,誰小就先取誰,取了以後在對應的數列中刪除這個數。
  4.   *        而後再進行比較,若是有數列爲空,那直接將另外一個數列的數據依次取出便可。
  5.   *        將兩個有序序列a[first...mid]和a[mid...last]合併
  6.   */
  7. void mergeArray(int a[], int first, int mid, int last, int tmp[]) 
  8. int i = first, j = mid + 1; 
  9. int k = 0; 
  10. while ( i <= mid && j <= last ) { 
  11. if ( a[i] <= a[j] ) 
  12.             tmp[k++] = a[i++]; 
  13. else
  14.             tmp[k++] = a[j++]; 
  15.     } 
  16. while ( i <= mid ) { 
  17.         tmp[k++] = a[i++]; 
  18.     } 
  19. while ( j <= last ) { 
  20.         tmp[k++] = a[j++]; 
  21.     } 
  22. for (i = 0; i < k; i++) {//這裏千萬不能丟了這個
  23.         a[first + i] = tmp[i]; 
  24.     } 
  25. /**
  26.   * @brief 歸併排序,其的基本思路就是將數組分紅二組A,B,若是這二組組內的數據都是有序的,
  27.   *        那麼就能夠很方便的將這二組數據進行排序。如何讓這二組組內數據有序了?
  28.   *        能夠將A,B組各自再分紅二組。依次類推,當分出來的小組只有一個數據時,
  29.   *        能夠認爲這個小組組內已經達到了有序,而後再合併相鄰的二個小組就能夠了。這樣經過先 (遞歸) 的分解數列,
  30.   *        再 (合併) 數列就完成了歸併排序。
  31.   */
  32. void mergeSort(int a[], int first, int last, int tmp[]) 
  33. int mid; 
  34. if ( first < last ) { 
  35.         mid = ( first + last ) / 2; 
  36.         mergeSort(a, first, mid, tmp);  //左邊有序
  37.         mergeSort(a, mid + 1, last, tmp);   //右邊有序
  38.         mergeArray(a, first, mid, last, tmp); 
  39.     } 

[cpp] view plaincopy

  1. /**
  2.   * @brief 雖然快速排序稱爲分治法,但分治法這三個字顯然沒法很好的歸納快速排序的所有步驟。
  3.   *        所以個人對快速排序做了進一步的說明:挖坑填數+分治法:
  4.   * @param R爲待排數組,low和high爲無序區
  5.   *        時間複雜度:最好O(nlogn),最壞O(n^2),平均O(nlogn),空間複雜度O(logn);
  6.   */
  7. void quickSort(int R[], int low, int high) 
  8. if ( low < high ) { 
  9. int i = low, j = high, temp = R[low]; 
  10. while ( i < j ) { 
  11. //從右往左掃描,若是數組元素大於temp,則繼續,直至找到第一個小於temp的元素
  12. while ( i < j && R[j] >= temp ) { 
  13.                 --j; 
  14.             } 
  15. if ( i < j ) { 
  16.                 R[i++] = R[j]; 
  17.             } 
  18. while ( i < j && R[i] <= temp ) { 
  19.                 ++i; 
  20.             } 
  21. if ( i < j ) { 
  22.                 R[j--] = R[i]; 
  23.             } 
  24.         } 
  25.         R[i] = temp; 
  26.         quickSort(R, low, i - 1); 
  27.         quickSort(R, i + 1, high); 
  28.     } 

各排序算法總體分析

冒泡排序、插入排序、希爾排序以及快速排序對數據的有序性比較敏感,尤爲是冒泡排序和插入排序;

選擇排序不關心表的初始次序,它的最壞狀況的排序時間與其最佳狀況沒多少區別,其比較次數爲 n(n-1)/2,但選擇排序能夠   很是有效的移動元素。所以對次序近乎正確的表,選擇排序可能比插入排序慢不少。

冒泡排序在最優狀況下只須要通過n-1次比較便可得出結果(即對於徹底正序的表),最壞狀況下也要進行n(n-1)/2 次比較,與選擇排序的比較次數相同,但數據交換的次數要多餘選擇排序,由於選擇排序的數據交換次數頂多爲 n-1,而冒泡排序最壞狀況下的數據交換n(n-1)/2 。冒泡排序不必定要進行 趟,但因爲它的記錄移動次數較多,因此它的平均時間性能比插入排序要差一些。

插入排序在最好的狀況下有最少的比較次數 ,可是它在元素移動方面效率很是低下,由於它只與毗鄰的元素進行比較,效率比較低。

希爾排序其實是預處理階段優化後的插入排序,通常而言,在 比較大時,希爾排序要明顯優於插入排序。

快速排序採用的「大事化小,小事化了」的思想,用遞歸的方法,將原問題分解成若干規模較小但與原問題類似的子問題進行求解。快速算法的平均時間複雜度爲O(nlogn) ,平均而言,快速排序是基於關鍵字比較的內部排序算法中速度最快者;可是因爲快速排序採用的是遞歸的方法,所以當序列的長度比較大時,對系統棧佔用會比較多。快速算法尤爲適用於隨機序列的排序。

所以,平均而言,對於通常的隨機序列順序表而言,上述幾種排序算法性能從低到高的順序大體爲:冒泡排序、插入排序、選擇排序、希爾排序、快速排序。但這個優劣順序不是絕對的,在不一樣的狀況下,甚至可能出現徹底的性能逆轉。

對於序列初始狀態基本有正序,可選擇對有序性較敏感的如插入排序、冒泡排序、選擇排序等方法

對於序列長度 比較大的隨機序列,應選擇平均時間複雜度較小的快速排序方法。

各類排序算法都有各自的優缺點,適應於不一樣的應用環境,所以在選擇一種排序算法解決實際問題以前,應當先分析實際問題的類型,再結合各算法的特色,選擇一種合適的算法

這裏特別介紹下快速排序:

快速排序的時間主要耗費在劃分操做上,對長度爲k的區間進行劃分,須要k-1次關鍵字比較。

(1)最壞的時間複雜度

最壞狀況是每次劃分選取的基準都是當前無序區中關鍵字最小(或最大)的記錄,劃分的結果是基準左邊的子區間爲空(或右邊的子區間爲空),而劃分所得的另外一個非空的子區間中記錄數目,僅僅比劃分前的的無序區中記錄個數減小一個。

    所以,快速排序必須作n-1次劃分,第i次劃分開始區間長度爲n-i+1,所需的比較次數爲n-i(1<=i<=n-1),故總的比較次數達到最大值:n(n-1)/2;

    若是按上面給出的劃分算法,每次取當前無序區的第1個記錄爲基準,那麼當文件的記錄已按遞增序(或遞減序)排列時,每次劃分所取的基準就是當前無序區中關鍵字最小(或最大)的記錄,則快速排序所需的比較次數反而最多。

(2)最壞的時間複雜度

在最好狀況下,每次劃分所取的基準都是當前無序區的"中值"記錄,劃分的結果是基準的左、右兩個無序子區間的長度大體相等。總的關鍵字比較次數:

        0(nlgn)

(3)平均時間複雜度

    儘管快速排序的最壞時間爲O(n2),但就平均性能而言,它是基於關鍵字比較的內部排序算法中速度最快者,快速排序亦所以而得名。它的平均時間複雜度爲O(nlgn)。

(4)空間複雜度

    快速排序在系統內部須要一個棧來實現遞歸。若每次劃分較爲均勻,則其遞歸樹的高度爲O(lgn),故遞歸後需棧空間爲O(lgn)。最壞狀況下,遞歸樹的高度爲O(n),所需的棧空間爲O(n)。

 

轉載:http://blog.csdn.net/hr10707020217/article/details/10581371

相關文章
相關標籤/搜索