數據結構-深刻淺出細談八大排序

1.排序的基本概念:

排序是各門語言中的核心,也是計算機數據處理中的核心運算,是咱們學過的「數據結構與算法」課程的重點。排序算法可以體現算法設計和算法分析的精神。有效的排序算法在一些算法(例如搜索算法與合併算法)中是重要的,如此這些算法才能獲得正確解答。 這篇博文主要包含了8大內部排序的算法複雜度,穩定性以及描述算法和可視化過程,花時間總結了好久,可是確定仍有不足,但願各位大神能指點迷津。 html

小注:剛發現,可視化過程的圖片是gif格式,可是傳上去以後好像不動,很差意思。請在點鏈接:可視化視圖視覺直觀感覺 7 種經常使用的排序算法(在最後的參考資料中也有) 算法

(1)排序算法的輸出必須遵照下列兩個原則:

a)輸出結果爲遞增串行(遞增是針對所需的排序順序而言); shell

b)輸出結果是原輸入的一種排列、或是重組。 數組

(2)被排序的對象-文件

被排序的對象--文件由一組記錄組成。記錄則由若干個數據項(或域)組成。其中有一項可用來標識一個記錄,稱爲關鍵字項。該數據項的值稱爲關鍵字(Key)。 數據結構

(3)排序運算的依據--關鍵字

  關鍵字,能夠是數字類型,也能夠是字符類型。 關鍵字的選取應根據問題的要求而定。 函數

2.排序的分類

1)按是否涉及內外存交換:

(1) 內部排序:

待排序的記錄所有存放在內存中進行排序的過程。 oop

(2) 外部排序:

待排序的記錄的數量很大,以致於內存不能容納所有記錄,在排序過程當中須要對外存進行訪問的排序過程。 性能

2) 按策略劃份內部排序方法

(1) 插入排序:

直接插入排序,折半插入排序; 測試

(2) 選擇排序:

簡單選擇排序,堆排序; spa

(3) 交換排序:

快速排序,冒泡排序;

(4) 歸併排序:

歸併排序;

(5) 分配排序:

基數排序;

3)按穩定性劃份內部排序

(1)穩定排序:

直接插入排序,冒泡排序,歸併排序,基數排序

(2)不穩定排序:

簡單選擇排序,希爾排序,快速排序,堆排序

3.內部排序算法的操做

大多數排序算法都有兩個基本的操做:比較和移動;
   (1) 比較兩個關鍵字的大小;

(2) 改變指向記錄的指針或移動記錄自己。

操做的實現依賴於待排序記錄的存儲方式(①待排序的記錄存放在連續的一組存儲單元上,相似於線性表的順序存儲;②待排序的記錄存放在靜態鏈表中;③待排序的記錄自己存儲在一組地址連續的存儲單元內,同時另設一個指示各個記錄存儲位置的地址向量,排序時不移動記錄自己,而是移動地址向量中這些記錄的地址)。

4.內部排序各算法代碼圖示詳解

(1) 冒泡排序

a)冒泡排序代碼:

  1. <SPAN style="FONT-SIZE: 14px">void bubbleSort(){
  2. for(i=0;i<n-1;i++){
  3. change=false;
  4. for(<SPAN style="COLOR: #333333">j=0;j<n-i-1;j++</SPAN>){
  5. if(a[j]>a[j+1]){
  6. a[j]←→a[j+1];
  7. change=true;
  8. }
  9. if(!change){
  10. break;
  11. }
  12. }
  13. }
  14. }</SPAN>
void bubbleSort(){ for(i=0;i<n-1;i++){ change=false; for(j=0;j<n-i-1;j++){ if(a[j]>a[j+1]){ a[j]←→a[j+1]; change=true; } if(!change){ break; } } } }


b)冒泡排序的可視化試圖:



(2) 選擇排序:

a)選擇排序代碼:

  1. <SPAN style="FONT-SIZE: 14px">void selectionSort(){
  2. for(i=0;i<n-1;i++){
  3. k=i;
  4. for(j=i+1;j<n;j++){
  5. if(a[j]<a[k]){
  6. k=j;
  7. }
  8. if(k!=i){
  9. a[i]←→a[k];
  10. }
  11. }
  12. }
  13. }</SPAN>
void selectionSort(){ for(i=0;i<n-1;i++){ k=i; for(j=i+1;j<n;j++){ if(a[j]<a[k]){ k=j; } if(k!=i){ a[i]←→a[k]; } } } }


b)選擇排序的可視化試圖:


(3) 插入排序

a)插入排序代碼:

  1. <SPAN style="FONT-SIZE: 14px">//直接插入排序
  2. void InsertSort(int array[], int size)
  3. {
  4. for(int i = 2; i < size; i++ )
  5. {
  6. if(array[i] < array[i-1])
  7. {
  8. array[0] = array[i];
  9. int j;
  10. for(j = i - 1; array[0] < array[j]; j--)
  11. {
  12. array[j+1] =array[j];
  13. }
  14. array[j+1] = array[0];
  15. }
  16. }
  17. for(int i = 1; i < size; i++)
  18. {
  19. cout << array[i] << endl;
  20. }
  21. }
  22. </SPAN>
//直接插入排序 void InsertSort(int array[], int size) { for(int i = 2; i < size; i++ ) { if(array[i] < array[i-1]) { array[0] = array[i]; int j; for(j = i - 1; array[0] < array[j]; j--) { array[j+1] =array[j]; } array[j+1] = array[0]; } } for(int i = 1; i < size; i++) { cout << array[i] << endl; } } 


(4) 快速排序

a)快速排序代碼:

  1. <SPAN style="FONT-SIZE: 14px">void QiuckSort(){
  2. If(low<high){
  3. Pivot=a[low];
  4. i=low;
  5. <SPAN style="WHITE-SPACE: pre"> </SPAN>j=high;
  6. <SPAN style="WHITE-SPACE: pre"> </SPAN>while(i<j){
  7. <SPAN style="WHITE-SPACE: pre"> </SPAN>while(i<j&&a[j]>=pivot){
  8. <SPAN style="WHITE-SPACE: pre"> </SPAN><SPAN style="WHITE-SPACE: pre"> </SPAN>j--;
  9. <SPAN style="WHITE-SPACE: pre"> </SPAN>a[i] ←→a[j];
  10. <SPAN style="WHITE-SPACE: pre"> </SPAN>}
  11. <SPAN style="WHITE-SPACE: pre"> </SPAN>while(i<j&&a[j]<=pivot){
  12. <SPAN style="WHITE-SPACE: pre"> </SPAN>i++;
  13. <SPAN style="WHITE-SPACE: pre"> </SPAN><SPAN style="WHITE-SPACE: pre"> </SPAN>a[i] ←→a[j];
  14. <SPAN style="WHITE-SPACE: pre"> </SPAN>}
  15. <SPAN style="WHITE-SPACE: pre"> </SPAN>a[i]=pivot;
  16. <SPAN style="WHITE-SPACE: pre"> </SPAN>QiuckSort(a,low,i-1);
  17. <SPAN style="WHITE-SPACE: pre"> </SPAN>QiuckSort(a,i-1,high);
  18. <SPAN style="WHITE-SPACE: pre"> </SPAN>}
  19. <SPAN style="WHITE-SPACE: pre"> </SPAN>}
  20. }
  21. </SPAN>
void QiuckSort(){ If(low<high){ Pivot=a[low]; i=low; j=high; while(i<j){ while(i<j&&a[j]>=pivot){ j--; a[i] ←→a[j]; } while(i<j&&a[j]<=pivot){ i++; a[i] ←→a[j]; } a[i]=pivot; QiuckSort(a,low,i-1); QiuckSort(a,i-1,high); } } } 

b)快速排序的可視化試圖:


(5)歸併排序

a)歸併排序代碼:

  1. <SPAN style="FONT-SIZE: 14px">//歸併操做
  2. void Merge(int sourceArr[], int targetArr[], int startIndex, int midIndex, int endIndex)
  3. {
  4. int i, j, k;
  5. for(i = midIndex+1, j = startIndex; startIndex <= midIndex && i <= endIndex; j++)
  6. {
  7. if(sourceArr[startIndex] < sourceArr[i])
  8. {
  9. targetArr[j] = sourceArr[startIndex++];
  10. }
  11. else
  12. {
  13. targetArr[j] = sourceArr[i++];
  14. }
  15. }
  16. if(startIndex <= midIndex)
  17. {
  18. for(k = 0; k <= midIndex-startIndex; k++)
  19. {
  20. targetArr[j+k] = sourceArr[startIndex+k];
  21. }
  22. }
  23. if(i <= endIndex)
  24. {
  25. for(k = 0; k <= endIndex- i; k++)
  26. {
  27. targetArr[j+k] = sourceArr[i+k];
  28. }
  29. }
  30. }
  31. //內部使用遞歸,空間複雜度爲n+logn
  32. void MergeSort(int sourceArr[], int targetArr[], int startIndex, int endIndex)
  33. {
  34. int midIndex;
  35. int tempArr[100]; //此處大小依需求更改
  36. if(startIndex == endIndex)
  37. {
  38. targetArr[startIndex] = sourceArr[startIndex];
  39. }
  40. else
  41. {
  42. midIndex = (startIndex + endIndex)/2;
  43. MergeSort(sourceArr, tempArr, startIndex, midIndex);
  44. MergeSort(sourceArr, tempArr, midIndex+1, endIndex);
  45. Merge(tempArr, targetArr,startIndex, midIndex, endIndex);
  46. }
  47. }
  48. //調用
  49. int _tmain(int argc, _TCHAR* argv[])
  50. {
  51. int a[8]={50,10,20,30,70,40,80,60};
  52. int b[8];
  53. MergeSort(a, b, 0, 7);
  54. for(int i = 0; i < sizeof(a) / sizeof(*a); i++)
  55. cout << b[i] << ' ';
  56. cout << endl;
  57. system("pause");
  58. return 0;
  59. }</SPAN>
//歸併操做 void Merge(int sourceArr[], int targetArr[], int startIndex, int midIndex, int endIndex) { int i, j, k; for(i = midIndex+1, j = startIndex; startIndex <= midIndex && i <= endIndex; j++) { if(sourceArr[startIndex] < sourceArr[i]) { targetArr[j] = sourceArr[startIndex++]; } else { targetArr[j] = sourceArr[i++]; } } if(startIndex <= midIndex) { for(k = 0; k <= midIndex-startIndex; k++) { targetArr[j+k] = sourceArr[startIndex+k]; } } if(i <= endIndex) { for(k = 0; k <= endIndex- i; k++) { targetArr[j+k] = sourceArr[i+k]; } } } //內部使用遞歸,空間複雜度爲n+logn void MergeSort(int sourceArr[], int targetArr[], int startIndex, int endIndex) { int midIndex; int tempArr[100]; //此處大小依需求更改 if(startIndex == endIndex) { targetArr[startIndex] = sourceArr[startIndex]; } else { midIndex = (startIndex + endIndex)/2; MergeSort(sourceArr, tempArr, startIndex, midIndex); MergeSort(sourceArr, tempArr, midIndex+1, endIndex); Merge(tempArr, targetArr,startIndex, midIndex, endIndex); } } //調用 int _tmain(int argc, _TCHAR* argv[]) { int a[8]={50,10,20,30,70,40,80,60}; int b[8]; MergeSort(a, b, 0, 7); for(int i = 0; i < sizeof(a) / sizeof(*a); i++) cout << b[i] << ' '; cout << endl; system("pause"); return 0; }

b)歸併排序的可視化試圖:

(6) 基數排序

a)基數排序的代碼:

  1. <SPAN style="FONT-SIZE: 14px">#include <stdlib.h>
  2. #include <math.h>
  3. testBS()
  4. {
  5. int a[] = {2,343,342,1,123,43,4343,433,687,654,3};
  6. intint *a_p = a;
  7. //計算數組長度
  8. int size = sizeof(a)/sizeof(int);
  9. //基數排序
  10. bucketSort3( a_p , size );
  11. //打印排序後結果
  12. int i ;
  13. for(i = 0 ; i < size ; i++ ) {
  14. printf("%d\n ",a[i]);
  15. }
  16. int t;
  17. scanf("%d",t);
  18. }
  19. //基數排序
  20. void bucketSort3(intint *p , int n)
  21. {
  22. //獲取數組中的最大數
  23. int maxNum = findMaxNum( p , n );
  24. //獲取最大數的位數,次數也是再分配的次數。
  25. int loopTimes = getLoopTimes(maxNum);
  26. int i ;
  27. //對每一位進行桶分配
  28. for( i = 1 ; i <= loopTimes ; i++) {
  29. sort2(p , n , i );
  30. }
  31. }
  32. //獲取數字的位數
  33. int getLoopTimes(int num)
  34. {
  35. int count = 1 ;
  36. int temp = num/10;
  37. while( temp != 0 ) {
  38. count++;
  39. temp = temp / 10;
  40. }
  41. return count;
  42. }
  43. //查詢數組中的最大數
  44. int findMaxNum( intint *p , int n)
  45. {
  46. int i ;
  47. int max = 0;
  48. for( i = 0 ; i < n ; i++) {
  49. if(*(p+i) > max) {
  50. max = *(p+i);
  51. }
  52. }
  53. return max;
  54. }
  55. //將數字分配到各自的桶中,而後按照桶的順序輸出排序結果
  56. void sort2(intint *p , int n , int loop)
  57. {
  58. //創建一組桶 此處的20是預設的 根據實際數狀況修改
  59. int buckets[10][20] = {} ;
  60. //求桶的index的除數
  61. //如798 個位桶index = ( 798 / 1 ) % 10 = 8
  62. // 十位桶index = ( 798 / 10 ) % 10 = 9
  63. // 百位桶index = ( 798 / 100 ) % 10 = 7
  64. // tempNum 爲上式中的一、十、100
  65. int tempNum = (int) pow(10 , loop-1);
  66. int i , j ;
  67. for( i = 0 ; i < n ; i++ ) {
  68. int row_index = (*(p+i) / tempNum) % 10;
  69. for(j = 0 ; j < 20 ; j++) {
  70. if(buckets[row_index][j] ==NULL) {
  71. buckets[row_index ][j] = *(p+i) ;
  72. break;
  73. }
  74. }
  75. }
  76. //將桶中的數,倒回到原有數組中
  77. int k = 0 ;
  78. for(i = 0 ; i < 10 ; i++) {
  79. for(j = 0 ; j < 20 ; j++) {
  80. if(buckets[i][j] != NULL) {
  81. *(p + k ) = buckets[i][j] ;
  82. buckets[i][j]=NULL;
  83. k++;
  84. }
  85. }
  86. }
  87. }</SPAN>
#include <stdlib.h> #include <math.h> testBS() { int a[] = {2,343,342,1,123,43,4343,433,687,654,3}; int *a_p = a; //計算數組長度 int size = sizeof(a)/sizeof(int); //基數排序 bucketSort3( a_p , size ); //打印排序後結果 int i ; for(i = 0 ; i < size ; i++ ) { printf("%d\n ",a[i]); } int t; scanf("%d",t); } //基數排序 void bucketSort3(int *p , int n) { //獲取數組中的最大數 int maxNum = findMaxNum( p , n ); //獲取最大數的位數,次數也是再分配的次數。 int loopTimes = getLoopTimes(maxNum); int i ; //對每一位進行桶分配 for( i = 1 ; i <= loopTimes ; i++) { sort2(p , n , i ); } } //獲取數字的位數 int getLoopTimes(int num) { int count = 1 ; int temp = num/10; while( temp != 0 ) { count++; temp = temp / 10; } return count; } //查詢數組中的最大數 int findMaxNum( int *p , int n) { int i ; int max = 0; for( i = 0 ; i < n ; i++) { if(*(p+i) > max) { max = *(p+i); } } return max; } //將數字分配到各自的桶中,而後按照桶的順序輸出排序結果 void sort2(int *p , int n , int loop) { //創建一組桶 此處的20是預設的 根據實際數狀況修改 int buckets[10][20] = {} ; //求桶的index的除數 //如798 個位桶index = ( 798 / 1 ) % 10 = 8 // 十位桶index = ( 798 / 10 ) % 10 = 9 // 百位桶index = ( 798 / 100 ) % 10 = 7 // tempNum 爲上式中的一、十、100 int tempNum = (int) pow(10 , loop-1); int i , j ; for( i = 0 ; i < n ; i++ ) { int row_index = (*(p+i) / tempNum) % 10; for(j = 0 ; j < 20 ; j++) { if(buckets[row_index][j] ==NULL) { buckets[row_index ][j] = *(p+i) ; break; } } } //將桶中的數,倒回到原有數組中 int k = 0 ; for(i = 0 ; i < 10 ; i++) { for(j = 0 ; j < 20 ; j++) { if(buckets[i][j] != NULL) { *(p + k ) = buckets[i][j] ; buckets[i][j]=NULL; k++; } } } }

(7)希爾排序(shell)

a)希爾排序代碼:

  1. <SPAN style="FONT-SIZE: 14px">void ShallSort(T a[] ,int n){
  2. d=n/2;
  3. while(d=){//一趟希爾排序,對d個序列分別進行插入排序
  4. for(i=d;i<n;i++){
  5. x=a[i];
  6. <SPAN style="WHITE-SPACE: pre"> </SPAN>}
  7. <SPAN style="WHITE-SPACE: pre"> </SPAN>for(j=i-d;j>=0&&x<a[j];j-=d){
  8. a[j+d]=a[j];
  9. a[j+d]=x;
  10. <SPAN style="WHITE-SPACE: pre"> </SPAN>}
  11. <SPAN style="WHITE-SPACE: pre"> </SPAN>d=d/2;
  12. <SPAN style="WHITE-SPACE: pre"> </SPAN>}
  13. <SPAN style="WHITE-SPACE: pre"> </SPAN>}
  14. </SPAN>
void ShallSort(T a[] ,int n){ d=n/2; while(d=){//一趟希爾排序,對d個序列分別進行插入排序 for(i=d;i<n;i++){ x=a[i]; } for(j=i-d;j>=0&&x<a[j];j-=d){ a[j+d]=a[j]; a[j+d]=x; } d=d/2; } } 

b)希爾排序的可視化試圖:


(8)堆排序

a)堆排序的可視化試圖:

  1. <SPAN style="FONT-SIZE: 14px">// array是待調整的堆數組,i是待調整的數組元素的位置,nlength是數組的長度
  2. //本函數功能是:根據數組array構建大根堆
  3. void HeapAdjust(int array[], int i, int nLength)
  4. {
  5. int nChild;
  6. int nTemp;
  7. for (nTemp = array[i]; 22 * i + 1 < nLength; i = nChild)
  8. {
  9. // 子結點的位置=2*(父結點位置)+ 1
  10. nChild = 22 * i + 1;
  11. // 獲得子結點中較大的結點
  12. if ( nChild < nLength-1 && array[nChild + 1] > array[nChild])
  13. ++nChild;
  14. // 若是較大的子結點大於父結點那麼把較大的子結點往上移動,替換它的父結點
  15. if (nTemp < array[nChild])
  16. {
  17. array[i] = array[nChild];
  18. array[nChild]= nTemp;
  19. }
  20. else
  21. // 不然退出循環
  22. break;
  23. }
  24. }
  25. // 堆排序算法
  26. void HeapSort(int array[],int length)
  27. {
  28. int tmp;
  29. // 調整序列的前半部分元素,調整完以後第一個元素是序列的最大的元素
  30. //length/2-1是第一個非葉節點,此處"/"爲整除
  31. for (int i = length / 2 - 1; i >= 0; --i)
  32. HeapAdjust(array, i, length);
  33. // 從最後一個元素開始對序列進行調整,不斷的縮小調整的範圍直到第一個元素
  34. for (int i = length - 1; i > 0; --i)
  35. {
  36. // 把第一個元素和當前的最後一個元素交換,
  37. // 保證當前的最後一個位置的元素都是在如今的這個序列之中最大的
  38. /// Swap(&array[0], &array[i]);
  39. tmp = array[i];
  40. array[i] = array[0];
  41. array[0] = tmp;
  42. // 不斷縮小調整heap的範圍,每一次調整完畢保證第一個元素是當前序列的最大值
  43. HeapAdjust(array, 0, i);
  44. }
  45. }</SPAN>
// array是待調整的堆數組,i是待調整的數組元素的位置,nlength是數組的長度 //本函數功能是:根據數組array構建大根堆 void HeapAdjust(int array[], int i, int nLength) { int nChild; int nTemp; for (nTemp = array[i]; 2 * i + 1 < nLength; i = nChild) { // 子結點的位置=2*(父結點位置)+ 1 nChild = 2 * i + 1; // 獲得子結點中較大的結點 if ( nChild < nLength-1 && array[nChild + 1] > array[nChild]) ++nChild; // 若是較大的子結點大於父結點那麼把較大的子結點往上移動,替換它的父結點 if (nTemp < array[nChild]) { array[i] = array[nChild]; array[nChild]= nTemp; } else // 不然退出循環 break; } } // 堆排序算法 void HeapSort(int array[],int length) { int tmp; // 調整序列的前半部分元素,調整完以後第一個元素是序列的最大的元素 //length/2-1是第一個非葉節點,此處"/"爲整除 for (int i = length / 2 - 1; i >= 0; --i) HeapAdjust(array, i, length); // 從最後一個元素開始對序列進行調整,不斷的縮小調整的範圍直到第一個元素 for (int i = length - 1; i > 0; --i) { // 把第一個元素和當前的最後一個元素交換, // 保證當前的最後一個位置的元素都是在如今的這個序列之中最大的 /// Swap(&array[0], &array[i]); tmp = array[i]; array[i] = array[0]; array[0] = tmp; // 不斷縮小調整heap的範圍,每一次調整完畢保證第一個元素是當前序列的最大值 HeapAdjust(array, 0, i); } }

5.內部排序算法的分析

(1)時間複雜度:

一個算法執行所耗費的時間,從理論上是不能算出來的,必須上機運行測試才能知道。但咱們不可能也沒有必要對每一個算法都上機測試,只需知道哪一個算法花費的時間多,哪一個算法花費的時間少就能夠了。而且一個算法花費的時間與算法中語句的執行次數成正比例,哪一個算法中語句執行次數多,它花費時間就多。一個算法中的語句執行次數稱爲語句頻度或時間頻度。記爲T(n)。

(2)空間複雜度:

一個 程序空間複雜度是指運行完一個程序所需內存的大小。利用 程序空間複雜度,能夠對程序的運行所須要的內存多少有個預先估計。一個 程序執行時除了須要 存儲空間和存儲自己所使用的指令、常數、 變量和輸入數據外,還須要一些對數據進行操做的工做單元和存儲一些爲現實計算所需信息的輔助空間。

(3)內部排序算法複雜度的總結:

6.內部排序算法的性能比較

1)排序的穩定性及分析:

假定在待排序的記錄序列中,存在多個具備相同的關鍵字的記錄,若通過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj以前,而在排序後的序列中,ri仍在rj以前,則稱這種排序算法是穩定的;不然稱爲不穩定的。

(1)冒泡排序

冒泡排序就是把小的元素往前調或者把大的元素日後調。比較是相鄰的兩個元素比較,交換也發生在這兩個元素之間。咱們知道,冒泡排序的交換條件是:a[j]>a[j+1]或者a[j]<a[j+1]很明顯不包括相等的狀況,因此若是兩個元素相等,他們不會交換;若是兩個相等的元素沒有相鄰,那麼即便經過前面的兩兩交換把兩個相鄰起來,這時候也不會交換,因此相同元素的先後順序不會改變,因此冒泡排序是一種穩定排序算法。

(2)選擇排序

選擇排序是給每一個位置選擇當前元素最小的,好比給第一個位置選擇最小的,在剩餘元素裏面給第二個元素選擇第二小的,依次類推,直到第n-1個元素,第n個元素不用選擇了,由於只剩下它一個最大的元素了。那麼,在一趟選擇,若是當前元素比一個元素小,而該小的元素又出如今一個和當前元素相等的元素後面,那麼交換後穩定性就被破壞了。比較拗口,舉個例子,序列5 8 5 2 9, 咱們知道第一遍選擇第1個元素5會和2交換,那麼原序列中2個5的相對先後順序就被破壞了,因此選擇排序不是一個穩定的排序算法。

(3)插入排序

插入排序是在一個已經有序的小序列的基礎上,一次插入一個元素。固然,剛開始這個有序的小序列只有1個元素,就是第一個元素。比較是從有序序列的末尾開始,也就是想要插入的元素和已經有序的最大者開始比起,若是比它大則直接插入在其後面,不然一直往前找直到找到它該插入的位置。若是遇見一個和插入元素相等的,那麼插入元素把想插入的元素放在相等元素的後面。即和冒泡排序同樣:a[j]>a[j+1]或者a[j]<a[j+1]很明顯不包括相等的狀況,因此若是兩個元素相等,他們不會交換;因此,相等元素的先後順序沒有改變,從原無序序列出去的順序就是排好序後的順序,因此插入排序是穩定的。

(4)快速排序

快速排序有兩個方向,左邊的i下標一直往右走,當a[i] <= [center_index](center_index中樞元素的數組下標),通常取爲數組第0個元素。而右邊的j下標一直往左走,當a[j] > a[center_index]。若是i和j都走不動了,i <= j, 交換a[i]和a[j],重複上面的過程,直到i>j。 交換a[j]和a[center_index],完成一趟快速排序。在中樞元素和a[j]交換的時候,頗有可能把前面的元素的穩定性打亂,好比序列爲 5,3,3,4,3,8,9,10,11, 如今中樞元素5和3(第5個元素,下標從1開始計)交換就會把元素3的穩定性打亂,因此快速排序是一個不穩定的排序算法,不穩定發生在中樞元素和a[j]交換的時刻。
快速排序是高效 排序算法了。實踐證實, 快速排序是全部排序算法中最高效的一種。它採用了分治的思想:先保證列表的前半部分都小於後半部分,而後分別對前半部分和後半部分 排序,這樣整個列表就有序了。這是一種先進的思想,也是它高效的緣由。由於在 排序算法中,算法的高效與否與列表中數字間的比較次數有直接的關係,而"保證列表的前半部分都小於後半部分"就使得前半部分的任何一個數今後之後都再也不跟後半部分的數進行比較了,大大減小了數字間沒必要要的比較。但查找數據得另當別論了。

(5)歸併排序

所謂「歸併」,試講兩個或兩個以上的有序文件合併成一個新的有序文件。歸併排序是把一個有n個記錄的無序文件當作是由n個長度爲1的有序子文件組成的文件,而後進行兩兩歸併,獲得[n/2]個長度爲2或1的有序文件,再兩兩歸併,如此重複,直至最後造成包含n個記錄的有序文件爲止。因此,歸併排序也是穩定的排序算法。

(6)基數排序

基數排序的思想是按組成關鍵字的各個數位的值進行排序,他是分配排序的一種。基數排序是按照低位先排序,而後收集;再按照高位排序,而後再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序,最後的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。爲了減小記錄的移動次數,隊列能夠採用鏈式存儲分配,稱爲鏈隊列。基數排序基於分別排序,分別收集,因此其是穩定的排序算法。

(7)希爾排序(shell)

希爾排序又稱爲「縮小增量排序」是按照不一樣步長對元素進行插入排序,當剛開始元素很無序的時候,步長最大,因此插入排序的元素個數不多,速度很快;當元素基本有序了,步長很小,插入排序對於有序的序列效率很高。關鍵步驟是取增量d,那全體記錄分紅d組,進行直接插入排序,直到d=1.因此,希爾排序的時間複雜度會比o(n^2)好一些。因爲屢次插入排序,咱們知道一次插入排序是穩定的,不會改變相同元素的相對順序,但在不一樣的插入排序過程當中,相同的元素可能在各自的插入排序中移動,最後其穩定性就會被打亂,因此shell排序是不穩定的。

(8)堆排序

咱們知道堆的結構是節點i的孩子爲2*i和2*i+1節點,大頂堆要求父節點大於等於其2個子節點,小頂堆要求父節點小於等於其2個子節點。在一個長爲n的序列,堆排序的過程是從第n/2開始和其子節點共3個值選擇最大(大頂堆)或者最小(小頂堆),這3個元素之間的選擇固然不會破壞穩定性。但當爲n /2-1, n/2-2, ...1這些個父節點選擇元素時,就會破壞穩定性。有可能第n/2個父節點交換把後面一個元素交換過去了,而第n/2-1個父節點把後面一個相同的元素沒有交換,那麼這2個相同的元素之間的穩定性就被破壞了。因此,堆排序不是穩定的排序算法。


參考資料:

1.http://v.youku.com/v_show/id_XNjIwNTEzMTA0.html15種排序算法可視化展現(神做!)
2.http://blog.jobbole.com/11745/視覺直觀感覺 7種經常使用的排序算法

3.數據結構(C語言版) 清華大學出版社 嚴蔚敏

4.軟件設計師教程 清華大學出版社 胡聖明

相關文章
相關標籤/搜索