數據結構基礎(4) --快速排序

 快速排序是最流行的,也是速度最快的排序算法(C++ STL 的sort函數就是實現的快速排序); 快速排序(Quicksort)是對冒泡排序的一種改進。由C. A. R. Hoare在1962年提出。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據序列變成有序序列。其算法的特點就是有一個樞軸(pivot), 樞軸左邊的元素都小於/等於樞軸所指向的元素, 樞軸右邊的元素都大於樞軸指向的元素;

 

快速排序算法思想:

    設要排序的數組是A[0], ..., A[N-1],首先任意選取一個數據作爲standard(通常選用數組的最後一個數)作爲關鍵數據,然後將所有比它小的數都放到它前面,所有比它大的數都放到它後面(其實只要保證所有比他小的元素都在其前面,則後一條件則自動滿足了),這個過程稱爲一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變動。(信息來源:百度百科)


一次劃分

目標:

    找一個記錄,以它的關鍵字/下標作爲”樞軸/pivot”,凡是值小於樞軸的元素均移動至該樞軸所指向的記錄之前,凡關鍵字大於樞軸的記錄均移動至該記錄之後。

    致使一趟排序之後,記錄的無序序列R[s..t]將分割成兩部分:R[s..i-1]和R[i+1..t],且  

       R[j].value ≤ R[i].value ≤ R[j].value

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //實現  
  2. template <typename Type>  
  3. int partitionBy3Loop(Type *array, int p, int r)  
  4. {  
  5.     int i = p;  
  6.     int j = r+1;    //j:超出末尾元素額下一位置  
  7.   
  8.     Type x = array[p];  //將最左邊的元素作爲樞軸元素  
  9.   
  10.     //將<x的元素交換到左邊區域  
  11.     //將>x的元素交換到右邊區域  
  12.     while (true)  
  13.     {  
  14.         //找到一個比x大(>=x)的元素  
  15.         while (i < r && array[++i] < x);  
  16.         //找到一個比x小(<=x)的元素  
  17.         while (array[--j] > x);  
  18.   
  19.         if (i >= j)  
  20.             break;  
  21.         //交換  
  22.         std::swap(array[i], array[j]);  
  23.     }  
  24.     //將樞軸元素與array[p]進行交換  
  25.     std::swap(array[p], array[j]);  
  26.   
  27.     //返回樞軸  
  28.     return j;  
  29. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. /**說明: 
  2.     幾乎國內所有的數據結構與算法的教材中的Partition實現都 
  3.     類似於上面的那一種, 雖然易於理解,但實現過於複雜; 
  4.     <算法導論>中給出了另一種實現方式, 
  5.     該方式雖然不易於理解(其實明白其原理之後你就會愛上她),但是比較容易實現! 
  6. */  
  7. template <typename Type>  
  8. int partitionBy1Loop(Type *array, int p, int r)  
  9. {  
  10.     Type x = array[r];  //x作爲最終樞軸所指向的元素  
  11.     //i指向的是樞軸左邊的最後一個元素  
  12.     //也就是與x左鄰元素的下標  
  13.     int i = p - 1;  
  14.     //j則不斷的尋找下一個<=x的元素  
  15.     for (int j = p; j < r; ++j)  
  16.     {  
  17.         if (array[j] <= x)  
  18.         {  
  19.             ++ i;  
  20.             std::swap(array[i], array[j]);  
  21.         }  
  22.     }  
  23.     std::swap(array[i+1], array[r]);  
  24.   
  25.     //最終使得所有(i+1)左邊的元素都<=array[i+1],  
  26.     //因此, 所有array[i+2:r]的元素都是大於array[i+1]的  
  27.   
  28.     return i+1;  
  29. }  

快速排序

首先對無序的記錄序列進行“一次劃分”,之後分別對分割所得兩個子序列“遞歸”進行快速排序。

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //實現  
  2. template <typename Type>  
  3. void quickSort(Type *array, int p, int r)  
  4. {  
  5.   
  6.     if (p < r)  
  7.     {  
  8.         int pivot = partitionBy1Loop(array, p, r);  
  9.         quickSort(array, p, pivot-1);  
  10.         quickSort(array, pivot+1, r);  
  11.     }  
  12. }  

快速排序的時間複雜性

假設一次劃分所得樞軸位置 i = k,則對 n 個記錄進行快排所需時間:

T(n) = {Tpass(n) + T(k-1) + T(n-k) |Tpass(n)爲對 n 個記錄進行一次劃分所需時間}

若待排序列中記錄的關鍵字是隨機分佈的,則 k 取 1 至 n 中任意一值的可能性相同。

由此可得快速排序所需時間的平均值爲:

  

設 Tavg(1)≤b,則可得結果:

 

因此:快速排序的時間複雜度爲O(nlogn)

  

   若待排記錄的初始狀態爲按關鍵字有序時,快速排序將蛻化爲起泡排序,其時間複雜度爲O(n^2)。

   爲避免出現這種情況,需在進行一次劃分之前,進行“預處理”,即:先對 R(s).key,  R(t).key 和 R[ë(s+t)/2û].key,進行相互比較,然後取關鍵字爲三個元素中居中間的那個元素作爲樞軸記錄。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章