天天看點

快速排序

今天介紹快速排序,這也是在實際中最常用的一種排序算法,速度快,效率高。就像名字一樣,快速排序是最優秀的一種排序算法。

思想

快速排序采用的思想是分治思想。

快速排序是找出一個元素(理論上可以随便找一個)作為基準(pivot),然後對數組進行分區操作,使基準左邊元素的值都不大于基準值,基準右邊的 元素值 都不小于基準值,如此作為基準的元素調整到排序後的正确位置。遞歸快速排序,将其他n-1個元素也調整到排序後的正确位置。最後每個元素都是在排序後的正 确位置,排序完成。是以快速排序算法的核心算法是分區操作,即如何調整基準的位置以及調整傳回基準的最終位置以便分治遞歸。

舉例說明一下吧,這個可能不是太好了解。假設要排序的序列為

2 2 4 9 3 6 7 1 5 首先用2當作基準,使用i j兩個指針分别從兩邊進行掃描,把比2小的元素和比2大的元素分開。首先比較2和5,5比2大,j左移

2 2 4 9 3 6 7 1 5 比較2和1,1小于2,是以把1放在2的位置

2 1 4 9 3 6 7 1 5 比較2和4,4大于2,是以将4移動到後面

2 1 4 9 3 6 7 4 5 比較2和7,2和6,2和3,2和9,全部大于2,滿足條件,是以不變

經過第一輪的快速排序,元素變為下面的樣子

[1] 2 [9 3 6 7 4 5]

之後,在把2左邊的元素進行快排,由于隻有一個元素,是以快排結束。右邊進行快排,遞歸進行,最終生成最後的結果。

代碼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<code>int</code> <code>quicksort(vector&lt;</code><code>int</code><code>&gt; &amp;v, </code><code>int</code> <code>left, </code><code>int</code> <code>right){</code>

<code>        </code><code>if</code><code>(left &lt; right){</code>

<code>                </code><code>int</code> <code>key = v[left];</code>

<code>                </code><code>int</code> <code>low = left;</code>

<code>                </code><code>int</code> <code>high = right;</code>

<code>                </code><code>while</code><code>(low &lt; high){</code>

<code>                        </code><code>while</code><code>(low &lt; high &amp;&amp; v[high] &gt; key){</code>

<code>                                </code><code>high--;</code>

<code>                        </code><code>}</code>

<code>                        </code><code>v[low] = v[high];</code>

<code>                        </code><code>while</code><code>(low &lt; high &amp;&amp; v[low] &lt; key){</code>

<code>                                </code><code>low++;</code>

<code>                        </code><code>v[high] = v[low]</code>

<code>                </code><code>}</code>

<code>                </code><code>v[low] = key;</code>

<code>                </code><code>quicksort(v,left,low-1);</code>

<code>                </code><code>quicksort(v,low+1,right);</code>

<code>        </code><code>}</code>

<code>}</code>

分析

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

最壞情況是每次劃分選取的基準都是目前無序區中關鍵字最小(或最大)的記錄,劃分的結果是基準左邊的子區間為空(或右邊的子區間為空),而劃分所得的另一個非空的子區間中記錄數目,僅僅比劃分前的無序區中記錄個數減少一個。時間複雜度為O(n*n)

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

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

一趟快速排序的算法是:

   1)、設定兩個變量I、J,排序開始的時候I:=1,J:=N;

   2)以第一個數組元素作為關鍵資料,指派給X,即X:=A[1];

   3)、從J開始向前搜尋,即由後開始向前搜尋(J:=J-1),找到第一個小于X的值,兩者交換;

   4)、從I開始向後搜尋,即由前開始向後搜尋(I:=I+1),找到第一個大于X的值,兩者交換;

   5)、重複第3、4步,直到I=J;

   例如:待排序的數組A的值分别是:(初始關鍵資料X:=49)

                   A[1]     A[2]     A[3]     A[4]     A[5]      A[6]     A[7]:

                     49        38       65       97       76       13        27

進行第一次交換後:   27        38       65       97       76       13        49

                   ( 按照算法的第三步從後面開始找

進行第二次交換後:   27        38       49       97       76       13        65

                  ( 按照算法的第四步從前面開始找&gt;X的值,65&gt;49,兩者交換,此時I:=3 )

進行第三次交換後:   27        38       13       97       76       49        65

( 按照算法的第五步将又一次執行算法的第三步從後開始找

進行第四次交換後:   27        38       13       49       76       97        65

( 按照算法的第四步從前面開始找大于X的值,97&gt;49,兩者交換,此時J:=4 )

      此時再執行第三不的時候就發現I=J,進而結束一躺快速排序,那麼經過一躺快速排序之後的結果是:27        38       13       49       76       97        65,即是以大于49的數全部在49的後面,是以小于49的數全部在49的前面。

快速排序法”使用的是遞歸原理,下面我結合一個例子來說明“快速排序法”的原理。首先給出一個數組 {53,12,98,63,18,72,80,46, 32,21},先找到第一個數--53,把它作為中間值,也就是說,要把53放在一個位置,使得它左邊的值比它小,右邊的值比它大。{21,12,32, 46,18,53,80,72,63,98},這樣一個數組的排序就變成了兩個小數組的排序--53左邊的數組和53右邊的數組,而這兩個數組繼續用同樣 的方式繼續下去,一直到順序完全正确。

     我這樣講你們是不是很胡塗,不要緊,我下面給出實作的兩個函數:

<code>/*</code>

<code>n就是需要排序的數組,left和right是你需要排序的左界和右界,</code>

<code>如果要排序上面那個數組,那麼left和right分别是0和9</code>

<code>*/</code>

<code>void</code> <code>quicksort(</code><code>int</code> <code>n[], </code><code>int</code> <code>left,</code><code>int</code> <code>right)</code>

<code>{</code>

<code>int</code> <code>dp;</code>

<code>if</code> <code>(left&lt;right) {</code>

<code>     </code><code>/*</code>

<code>     </code><code>這就是下面要講到的函數,按照上面所說的,就是把所有小于53的數放</code>

<code>     </code><code>到它的左邊,大的放在右邊,然後傳回53在整理過的數組中的位置。</code>

<code>     </code><code>*/</code>

<code>     </code><code>dp=partition(n,left,right);</code>

<code>     </code><code>quicksort(n,left,dp-1);</code>

<code>     </code><code>quicksort(n,dp+1,right); </code><code>//這兩個就是遞歸調用,分别整理53左邊的數組和右邊的數組</code>

<code>    </code><code>}</code>

     我們上面提到先定位第一個數,然後整理這個數組,把比這個數小的放到它的左邊,大的放右邊,然後傳回這中間值的位置,下面這函數就是做這個的。

<code>int</code> <code>partition(</code><code>int</code> <code>n[],</code><code>int</code> <code>left,</code><code>int</code> <code>right)</code>

<code>    </code><code>int</code> <code>lo,hi,pivot,t;</code>

<code>    </code><code>pivot=n[left];</code>

<code>    </code><code>lo=left-1;</code>

<code>    </code><code>hi=right+1;</code>

<code>    </code><code>while</code><code>(lo+1!=hi) {</code>

<code>        </code><code>if</code><code>(n[lo+1]&lt;=pivot)</code>

<code>            </code><code>lo++;</code>

<code>        </code><code>else</code> <code>if</code><code>(n[hi-1]&gt;pivot)</code>

<code>             </code><code>hi--;</code>

<code>        </code><code>else</code> <code>{</code>

<code>            </code><code>t=n[lo+1];</code>

<code>            </code><code>n[++lo]=n[hi-1];</code>

<code>            </code><code>n[--hi]=t;</code>

<code>    </code><code>n[left]=n[lo];</code>

<code>    </code><code>n[lo]=pivot;</code>

<code>    </code><code>return</code> <code>lo;</code>

     這段程式并不難,應該很好看懂,我把過程大緻講一下,首先你的腦子裡先浮現一個數組和三個指針,第一個指針稱為p指針,在整個過程結束之前它牢牢的指向第一個數,第二個指針和第三個指針分别為lo指針和hi指針,分别指向最左邊的值和最右邊的值。lo指針和hi指針從兩邊同時向中間逼近,在逼近的過程中不停的與p指針的值比較,如果lo指針的值比p指針的值小,lo++,還小還++,再小再++,直到碰到一個大于p指針的值,這時視線轉移到hi指針,如果 hi指針的值比p指針的值大,hi--,還大還--,再大再--,直到碰到一個小于p指針的值。這時就把lo指針的值和hi指針的值做一個調換。持續這過程直到兩個指針碰面,這時把p指針的值和碰面的值做一個調換,然後傳回p指針新的位置。

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

<code>#include&lt;iostream&gt; </code>

<code>using</code> <code>namespace</code> <code>std; </code>

<code>int</code> <code>a[200001],n; </code>

<code>void</code> <code>swap(</code><code>int</code> <code>&amp;a,</code><code>int</code> <code>&amp;b)</code>

<code>{ </code>

<code>    </code><code>int</code> <code>tmp = a; </code>

<code>    </code><code>a = b; </code>

<code>    </code><code>b = tmp; </code>

<code>} </code>

<code>int</code> <code>partition(</code><code>int</code> <code>p,</code><code>int</code> <code>r)</code>

<code>    </code><code>int</code> <code>rnd = </code><code>rand</code><code>()%(r-p+1)+p; </code>

<code>    </code><code>swap(a[rnd],a[r]); </code>

<code>    </code><code>int</code> <code>pvt = r, i = p-1; </code>

<code>    </code><code>for</code><code>(</code><code>int</code> <code>j = i+1;j&lt;r;j++) </code>

<code>    </code><code>if</code><code>(a[j]&lt;a[pvt]) </code>

<code>    </code><code>swap(a[j],a[++i]); </code>

<code>    </code><code>swap(a[++i],a[pvt]); </code>

<code>    </code><code>return</code> <code>i; </code>

<code>void</code> <code>qsort</code><code>(</code><code>int</code> <code>p,</code><code>int</code> <code>r)</code>

<code>    </code><code>if</code><code>(p&lt;r){ </code>

<code>        </code><code>int</code> <code>q = partition(p,r); </code>

<code>        </code><code>qsort</code><code>(p,q-1); </code>

<code>        </code><code>qsort</code><code>(q+1,r); </code>

<code>    </code><code>} </code>

<code>int</code> <code>main()</code>

<code>    </code><code>cin&gt;&gt;n; </code>

<code>    </code><code>for</code><code>(</code><code>int</code> <code>i=0;i&lt;n;i++) </code>

<code>    </code><code>cin&gt;&gt;a[i]; </code>

<code>    </code><code>qsort</code><code>(0,n-1); </code>

<code>    </code><code>for</code><code>( i=0;i&lt;n;i++) </code>

<code>    </code><code>cout&lt;&lt;a[i]&lt;&lt;</code><code>" "</code><code>; </code>

<code>    </code><code>return</code> <code>0; </code>

本文轉自夏雪冬日部落格園部落格,原文連結:http://www.cnblogs.com/heyonggang/p/3145384.html,如需轉載請自行聯系原作者

繼續閱讀