高速排序演算法

來源:互聯網
上載者:User

標籤:style   class   blog   http   tar   color   

高速排序演算法

 

作者 July  二零一一年一月四日
------------------------------------------

   寫之前,先說點題外話。
每寫一篇文章,我都會遵循下面幾點原則:
一、保持版面的盡量清晰,力保排版良好。
二、力爭所寫的東西,清晰易懂,圖文並茂
三、盡最大可能確保所寫的東西精準,有實用價值。

   由於,我認為,你既然要把你的文章,發布出來,那麼你就一定要為你的讀者負責。
不然,就不要發表出來。一切,為讀者服務。

   ok,閑不多說。接下來,咱們立馬進入本文章的主題,排序演算法。
眾所周知,高速排序演算法是排序演算法中的重頭戲。
因此,本系列,本文就從高速排序開始。
------------------------------------------------------

一、高速排序演算法的基本特性
時間複雜度:O(n*lgn)
最壞:O(n^2)
空間複雜度:O(n*lgn)
不穩定。

高速排序是一種排序演算法,對包括n個數的輸入數組,平均時間為O(nlgn),最壞情況是O(n^2)。
一般是用於排序的最佳選擇。由於,基於比較的排序,最快也僅僅能達到O(nlgn)。


二、高速排序演算法的描寫敘述
演算法導論,第7章
高速排序時基於分治模式處理的,
對一個典型子數組A[p...r]排序的分治過程為三個步驟:
1.分解:
A[p..r]被劃分為倆個(可能空)的子數組A[p ..q-1]和A[q+1 ..r],使得
A[p ..q-1] <= A[q] <= A[q+1 ..r]
2.解決:通過遞迴調用高速排序,對子數組A[p ..q-1]和A[q+1 ..r]排序。
3.合并。

 

三、高速排序演算法

版本號碼一:
QUICKSORT(A, p, r)
1 if p < r
2    then q ← PARTITION(A, p, r)   //關鍵
3         QUICKSORT(A, p, q - 1)
4         QUICKSORT(A, q + 1, r)

數組劃分
高速排序演算法的關鍵是PARTITION過程,它對A[p..r]進行就地重排:
PARTITION(A, p, r)
1  x ← A[r]
2  i ← p - 1
3  for j ← p to r - 1
4       do if A[j] ≤ x
5             then i ← i + 1
6                  exchange A[i] <-> A[j]
7  exchange A[i + 1] <-> A[r]
8  return i + 1

 

ok,咱們來舉一個詳細而完整的範例。
來對下面數組,進行高速排序,
  2   8   7   1   3   5   6   4(主元)


一、

i p/j

  2   8   7   1   3   5   6   4(主元)
j指的2<=4,於是i++,i也指到2,2和2互換,原數組不變。
j後移,直到指向1..
二、
              j(指向1)<=4,於是i++
i指向了8,所以8與1交換。
數組變成了:
       i          j
  2   1   7   8   3   5   6   4
三、j後移,指向了3,3<=4,於是i++
i這是指向了7,於是7與3交換。
數組變成了:
             i         j
  2   1   3   8   7   5   6   4
四、j繼續後移,發現沒有再比4小的數,所以,執行到了最後一步,
即上述PARTITION(A, p, r)代碼部分的 第7行。
因此,i後移一個單位,指向了8
                 i               j
  2   1   3   8   7   5   6   4
A[i + 1] <-> A[r],即8與4交換,所以,數組終於變成了例如以下形式,
  2   1   3   4   7   5   6   8
ok,高速排序第一趟完畢。


4把整個數組分成了倆部分,2 1 3,7 5 6 8,再遞迴對這倆部分分別高速排序。
i p/j
  2   1   3(主元)
  2與2互換,不變,然後又是1與1互換,還是不變,最後,3與3互換,不變,
終於,3把2 1 3,分成了倆部分,2 1,和3.
再對2 1,遞迴排序,終於結果成為了1 2 3.

7 5 6 8(主元),7、5、6、都比8小,所以第一趟,還是7 5 6 8,
只是,此刻8把7 5 6 8,分成了  7 5 6,和8.[7 5 6->5 7 6->5 6 7]
再對7 5 6,遞迴排序,終於結果變成5 6 7 8。

ok,全部過程,全部分析完畢。
最後,看下我畫的圖:

 

高速排序演算法版本號碼二

只是,這個版本號碼不再選取(如上第一版本號碼的)數組的最後一個元素為主元,
而是選擇,數組中的第一個元素為主元。

/**************************************************/
/*  函數功能:高速排序演算法                        */
/*  函數參數:結構類型table的指標變數tab          */
/*            整型變數left和right左右邊界的下標   */
/*  函數傳回值:空                                */
/*  檔案名稱:quicsort.c  函數名:quicksort ()      */
/**************************************************/
void quicksort(table *tab,int left,int right)
{
  int i,j;
  if(left<right)
  {
    i=left;j=right;
    tab->r[0]=tab->r[i]; //準備以本次最左邊的元素值為標準進行劃分,先儲存其值
    do
    {
      while(tab->r[j].key>tab->r[0].key&&i<j)
        j--;        //從右向左找第1個小於標準值的位置j
      if(i<j)                               //找到了,位置為j
      {
        tab->r[i].key=tab->r[j].key;i++;
      }           //將第j個元素置於左端並重設i
      while(tab->r[i].key<tab->r[0].key&&i<j)
        i++;      //從左向右找第1個大於標準值的位置i
      if(i<j)                       //找到了,位置為i
      {
        tab->r[j].key=tab->r[i].key;j--;
      }           //將第i個元素置於右端並重設j
    }while(i!=j);

    tab->r[i]=tab->r[0];         //將標準值放入它的終於位置,本次劃分結束
    quicksort(tab,left,i-1);     //對標準值左半部遞迴調用本函數
    quicksort(tab,i+1,right);    //對標準值右半部遞迴調用本函數
  }
}

----------------

ok,咱們,還是以上述相同的數組,應用此快排演算法的版本號碼二,來示範此排序過程:
這次,以數組中的第一個元素2為主元。
  2(主)  8  7  1  3  5  6  4

請細看:
  2  8  7  1  3  5  6  4
  i->                     <-j
   (找大)               (找小)
一、j
j找第一個小於2的元素1,1賦給(覆蓋重設)i所指元素2
得到:
  1  8  7     3  5  6  4
      i       j     
     
二、i
i找到第一個大於2的元素8,8賦給(覆蓋重設)j所指元素(NULL<-8)
  1     7  8  3  5  6  4
      i   <-j

三、j
j繼續左移,在與i碰頭之前,沒有找到比2小的元素,結束。
最後,主元2補上。
第一趟快排結束之後,數組變成:
  1  2  7  8  3  5  6  4

第二趟,
        7  8  3  5  6  4
        i->             <-j
         (找大)        (找小)
 
一、j
j找到4,比主元7小,4賦給7所處位置
得到:
        4  8  3  5  6  
        i->                j
二、i
i找比7大的第一個元素8,8覆蓋j所指元素(NULL)
        4     3  5  6  8
            i          j
        4  6  3  5     8
            i->       j
                 i與j碰頭,結束。
第三趟:
        4  6  3  5  7  8
......
下面,分析原理,一致,略過。
最後的結果,例如以所看到的:
  1  2  3  4  5  6  7  8
相信,經過以上內容的詳細分析,你一定明了了。

最後,貼一下我畫的關於這個排序過程的圖: 

  

完。一月五日補充。
OK,上述倆種演算法,明確一種就可以。
-------------------------------------------------------------

 

五、高速排序的最壞情況和最快情況。
最壞情況發生在劃分過程產生的倆個地區分別包括n-1個元素和一個0元素的時候,
即假設演算法每一次遞迴調用過程中都出現了,這樣的劃分不正確稱。那麼劃分的代價為O(n),
由於對一個大小為0的數組遞迴調用後,返回T(0)=O(1)。
估演算法的執行時間能夠遞迴的表示為:

    T(n)=T(n-1)+T(0)+O(n)=T(n-1)+O(n).
能夠證明為T(n)=O(n^2)。

因此,假設在演算法的每一層遞迴上,劃分都是最大程度不正確稱的,那麼演算法的執行時間就是O(n^2)。
亦即,高速排序演算法的最壞情況並不比插入排序的更好。

此外,當數組全然排好序之後,高速排序的執行時間為O(n^2)。
而在相同情況下,插入排序的執行時間為O(n)。

//注,請注意理解這句話。我們說一個排序的時間複雜度,是僅僅針對一個元素的。
//意思是,把一個元素進行插入排序,即把它插入到有序的序列裡,花的時間為n。

 
再來證明,最快情況下,即PARTITION可能做的最平衡的劃分中,得到的每一個子問題都不能大於n/2.
由於當中一個子問題的大小為|_n/2_|。還有一個子問題的大小為|-n/2-|-1.
在這樣的情況下,高速排序的速度要快得多。為,
      T(n)<=2T(n/2)+O(n).能夠證得,T(n)=O(nlgn)。

直觀上,看,高速排序就是一顆遞迴數,當中,PARTITION總是產生9:1的劃分,
總的執行時間為O(nlgn)。各結點中示出了子問題的規模。每一層的代價在右邊顯示。
每一層包括一個常數c。

完。
July、二零一一年一月四日。
=============================================

請各位自行,思考下面這個版本號碼,相應於上文哪個版本號碼?
     HOARE-PARTITION(A, p, r)
 1  x ← A[p]
 2  i ← p - 1
 3  j ← r + 1
 4  while TRUE
 5      do repeat j ← j - 1
 6           until A[j] ≤ x
 7         repeat i ← i + 1
 8           until A[i] ≥ x
 9         if i < j
10            then exchange A[i] ? A[j]
11            else return j

 

   我經常思考,為什麼有的人當時明明讀懂明確了一個演算法,
而一段時間過後,它又對此演算法全然陌生而不瞭解了列?

   我想,究其根本,還是沒有徹底明確此高速排序演算法的原理,與來龍去脈...
那作何改進列,僅僅能找發明那個演算法的原作者了,從原作者身上,再多挖掘點實用的東西出來。

        July、二零一一年二月十五日更新。

=========================================
最後,再給出一個高速排序演算法的簡潔示範範例:
    Quicksort函數
void quicksort(int l, int u)
{   int i, m;
    if (l >= u) return;
    swap(l, randint(l, u));
    m = l;
    for (i = l+1; i <= u; i++)
        if (x[i] < x[l])
            swap(++m, i);
    swap(l, m);
    quicksort(l, m-1);
    quicksort(m+1, u);
}

   假設函數的調用形式是quicksort(0, n-1),那麼這段代碼將對一個全域數組x[n]進行排序。
函數的兩個參數各自是將要進行排序的子數組的下標:l是較低的下標,而u是較高的下標。

   函數調用swap(i,j)將會交換x[i]與x[j]這兩個元素。
第一次交換操作將會依照均勻分布的方式在l和u之間隨機地選擇一個劃分元素。

   ok,很多其它請參考我寫的關於高速排序演算法的第二篇文章:一之續、高速排序演算法的深入分析,第三篇文章:十二、一之再續:高速排序演算法之全部版本號碼的c/c++實現

                   July、二零一一年二月二十日更新。

  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.