排序算法3

1、序言

這是《漫談經典排序算法系列》第三篇,先解析了冒泡排序,然後引出快速排序,給出了快速排序的兩種實現版本

各種排序算法的解析請參考如下:

《漫談經典排序算法:一、從簡單選擇排序到堆排序的深度解析》

《漫談經典排序算法:二、各種插入排序解析及性能比較》

《漫談經典排序算法:三、冒泡排序 && 快速排序》

《漫談經典排序算法:四、歸併排序》

《漫談經典排序算法:五、線性時間排序(計數、基數、桶排序)》

《漫談經典排序算法:六、各種排序算法總結》

 

注:爲了敘述方便,本文以及源代碼中均不考慮A[0],默認下標從1開始。

2、冒泡排序

          2.1 引出

           前面的兩篇博客裏講的插入排序是基於“逐個記錄插入”,選擇排序是基於“選擇”,那麼冒泡排序其實是基於“交換”。每次從第一個記錄開始,一、二兩個記錄比較,大的往後放,二三兩個記錄比較...依次類推,這就是一趟冒泡排序。每一趟冒泡排序後,無序序列中值最大的記錄冒到序列末尾,所以稱之爲冒泡排序。

          2.2 代碼

[cpp] view plain copy
  1. //冒泡排序  
  2. void bubbleSort(int *a,int n)  
  3. {  
  4.     int i,j;  
  5.     for(i=1;i<n;i++)  
  6.         for(j=1;j<n-i+1;j++){  
  7.             if(a[j+1]<a[j]){  
  8.                 a[j]=a[j]+a[j+1];  
  9.                 a[j+1]=a[j]-a[j+1];  
  10.                 a[j]=a[j]-a[j+1];  
  11.             }  
  12.         }  
  13. }  

          2.3 效率分析

相對於簡單選擇排序,冒泡排序交換次數明顯更多。它是通過不斷地交換把最大的數冒出來。冒泡排序平均時間和最壞情況下(逆序)時間爲o(n^2)。最佳情況下雖然不用交換,但比較的次數沒有減少,時間複雜度仍爲o(n^2)。此外冒泡排序是穩定的。

3、快速排序

          3.1 引出

           快速排序是冒泡排序的一種改進,冒泡排序排完一趟是最大值冒出來了,那麼可不可以先選定一個值,然後掃描待排序序列,把小於該值的記錄和大於該值的記錄分成兩個單獨的序列,然後分別對這兩個序列進行上述操作。這就是快速排序,我們把選定的那個值稱爲樞紐值,如果樞紐值爲序列中的最大值,那麼一趟快速排序就變成了一趟冒泡排序。

          3.2 代碼

           兩種版本,第一種是參考《數據結構》,在網上這種寫法很流行。第二種是參考《算法導論》,實現起來較複雜。

[cpp] view plain copy
  1. //快速排序(兩端交替着向中間掃描)  
  2. void quickSort1(int *a,int low,int high)  
  3. {  
  4.     int pivotkey=a[low];//以a[low]爲樞紐值  
  5.     int i=low,j=high;  
  6.     if(low>=high)  
  7.         return;  
  8.     //一趟快速排序  
  9.     while(i<j){//雙向掃描  
  10.         while(i < j && a[j] >= pivotkey)  
  11.             j--;  
  12.         a[i]=a[j];  
  13.         while(i < j && a[i] <= pivotkey)  
  14.             i++;  
  15.         a[j]=a[i];  
  16.     }  
  17.     a[i]=pivotkey;//放置樞紐值  
  18.     //分別對左邊、右邊排序  
  19.     quickSort1(a,low,i-1);   
  20.     quickSort1(a,i+1,high);   
  21. }  
  22.   
  23. //快速排序(以最後一個記錄的值爲樞紐值,單向掃描數組)  
  24. void quickSort2(int *a,int low,int high)  
  25. {  
  26.     int pivotkey=a[high];//以a[high]爲樞紐值  
  27.     int i=low-1,temp,j;  
  28.     if(low>=high)  
  29.         return;  
  30.     //一趟快速排序  
  31.     for(j=low;j<high;j++){  
  32.         if(a[j]<=pivotkey){  
  33.             i++;  
  34.             temp=a[i];  
  35.             a[i]=a[j];  
  36.             a[j]=temp;  
  37.         }  
  38.     }  
  39.     i++;  
  40.     //放置樞紐值  
  41.     temp=a[i];  
  42.     a[i]=pivotkey;  
  43.     a[high]=temp;  
  44.     //分別對左邊、右邊排序  
  45.     quickSort2(a,low,i-1);   
  46.     quickSort2(a,i+1,high);   
  47. }  

          3.3 效率分析

        快速排序時間與劃分是否對稱有關。快速排序的平均時間複雜度爲o(n*logn),至於爲什麼是o(n*logn),請參考《算法導論》第7章,書中用遞歸樹的方法闡述了快速排序平均時間。且常數因子很小,所以就平均時間而言,快速排序是很好的內部排序方法。最佳情況下(每次劃分都對稱)時間複雜度o(n*logn)。最壞情況下(每次劃分都不對稱,如輸入的序列有序或者逆序時)時間複雜度爲o(n^2),所以在待排序序列有序或逆序時不宜選用快速排序。此外,快速排序是不穩定的。

        最佳情況下,每次劃分都是對稱的,由於樞紐值不再考慮,所以得到的兩個子問題的大小不可能大於n/2,同時一趟快速排序時間爲o(n),所以運行時間遞歸表達式:

T(n)<=2T(n/2)+o(n)。這個遞歸式的解法請參考下一篇博客中歸併排序效率分析。其解爲T(n)=o(n*logn)。

        最壞情況下,每次劃分都很不對稱,T(n)=T(n-1)+o(n),可以用遞歸樹來解,第i層的代價爲n-i+1.總共有n層。把每一層代價加起來有n-1個n相加。所以這個遞歸式的解爲T(n)=o(n^2),此時就是冒泡排序。

4、附錄

         4.1 參考書籍

          《數據結構》嚴蔚敏版                    《算法導論》

           4.2 所有源代碼

[cpp] view plain copy
  1. #include<stdio.h>  
  2.   
  3. //冒泡排序  
  4. void bubbleSort(int *a,int n)  
  5. {  
  6.     int i,j;  
  7.     for(i=1;i<n;i++)  
  8.         for(j=1;j<n-i+1;j++){  
  9.             if(a[j+1]<a[j]){  
  10.                 a[j]=a[j]+a[j+1];  
  11.                 a[j+1]=a[j]-a[j+1];  
  12.                 a[j]=a[j]-a[j+1];  
  13.             }  
  14.         }  
  15. }  
  16.   
  17. void main()  
  18. {  
  19.     int i;  
  20.     int a[7]={0,3,5,8,9,1,2};//不考慮a[0]  
  21.     bubbleSort(a,6);  
  22.     for(i=1;i<=6;i++)  
  23.         printf("%-4d",a[i]);  
  24.     printf("\n");  
  25. }  
[cpp] view plain copy
  1. #include<stdio.h>  
  2.   
  3. //快速排序(兩端交替着向中間掃描)  
  4. void quickSort1(int *a,int low,int high)  
  5. {  
  6.     int pivotkey=a[low];//以a[low]爲樞紐值  
  7.     int i=low,j=high;  
  8.     if(low>=high)  
  9.         return;  
  10.     //一趟快速排序  
  11.     while(i<j){//雙向掃描  
  12.         while(i < j && a[j] >= pivotkey)  
  13.             j--;  
  14.         a[i]=a[j];  
  15.         while(i < j && a[i] <= pivotkey)  
  16.             i++;  
  17.         a[j]=a[i];  
  18.     }  
  19.     a[i]=pivotkey;//放置樞紐值  
  20.     //分別對左邊、右邊排序  
  21.     quickSort1(a,low,i-1);   
  22.     quickSort1(a,i+1,high);   
  23. }  
  24.   
  25. //快速排序(以最後一個記錄的值爲樞紐值,單向掃描數組)  
  26. void quickSort2(int *a,int low,int high)  
  27. {  
  28.     int pivotkey=a[high];//以a[high]爲樞紐值  
  29.     int i=low-1,temp,j;  
  30.     if(low>=high)  
  31.         return;  
  32.     //一趟快速排序  
  33.     for(j=low;j<high;j++){  
  34.         if(a[j]<=pivotkey){  
  35.             i++;  
  36.             temp=a[i];  
  37.             a[i]=a[j];  
  38.             a[j]=temp;  
  39.         }  
  40.     }  
  41.     i++;  
  42.     //放置樞紐值  
  43.     temp=a[i];  
  44.     a[i]=pivotkey;  
  45.     a[high]=temp;  
  46.     //分別對左邊、右邊排序  
  47.     quickSort2(a,low,i-1);   
  48.     quickSort2(a,i+1,high);   
  49. }  
  50.   
  51. void main()  
  52. {  
  53.     int i;  
  54.     int a[7]={0,3,5,8,9,1,2};//不考慮a[0]  
  55.     quickSort2(a,1,6);  
  56.     quickSort1(a,1,6);  
  57.     for(i=1;i<=6;i++)  
  58.         printf("%-4d",a[i]);  
  59.     printf("\n");  
  60. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章