深度剖析快速排序

2022-09-07 17:24:17 字數 3498 閱讀 5517

快速排序是乙個比較重要的排序演算法,本文中主要講解快排的遞迴非遞迴寫法,,兩種partition的方法和對快速排序的部分優化

快速排序是對氣泡排序的一種改進,通過一趟排序將排序的資料分割成獨立的兩個部分,其中一部分的所有資料比另一部分的所有資料都要小,在按這種方法對這兩部分資料分別進行快速排序,整個排序的過程是遞迴進行的,使得整個資料變成有序序列。

其實快排就是乙個用基準數,將乙個無序序列變成乙個兩部分整體有序,也就是在基準數之前的都小於基準數,後面的都大於基準數,所以整個演算法的關鍵之處在於將那個基準數找的自己的位置,我這通常稱這個過程為一次partition,一次partition後基準數的位置就已經被確定了,我們只需要再次遞迴的將基準數前的序列快速排序一次,基準數後的序列快速排序一次,這樣我們就可以完成快速排序了。

根據上面所說的,快速排序其實很簡單,我們用遞迴寫一下,就是找到基準數,對於的基準數我們一般找第乙個數,這裡確實基準數的選擇關係中快速排序的速度問題下面會說這個事情,然後我們partition一下,最後對前序列快速排序,對後序列快速排序即可。看下面**是不是很容易理解

/**

** @param a 需要排序的陣列

* @param low 陣列的起點

* @param high 陣列的終點

*/public

void

quicksort

(int

a,int low,

int high)

if(p< high -1)

}

所以這裡的關鍵就是這個partition函式怎麼寫了,這裡分享兩種partition的寫法。

public

intpartition2

(int a,

int low,

int high)

while

(low

<= temp)

++low;

//向後找乙個大於基準數的數

if(low

} a[low]

= temp;

//將基準數放在確定的位置

return low;

}

實際這是乙個什麼過程呢?請看下面的圖:

第二種的partition操作是乙個比較巧妙的操作,只要我們明白partition是做什麼的了,直接看**:

public

intpartition

(int

a,int low,

int high)

//基準數與a[j]交換

a[low]

= a[j]

; a[j]

= x;

return j;

}

同樣我們來看圖理解一下:

這兩種partition演算法都可以的,第二種演算法還是比較巧妙的最後都是將基準數把序列劃分出兩部分。

其實遞迴的基本演算法就是剛開始看見的那幾行**是不是很簡單,我們這裡要實現一下他的非遞迴演算法:

其實非遞迴演算法就是利用棧來代替掉系統棧所做的操作,整個非遞迴的思路就是依次將low p-1 p+1 high 這四個關鍵的位置一直入棧(其中p就是基準數的位置),實現遞迴中排序的過程。

/**

* 快排非遞迴

* @param a 待排的序列

* @param low 陣列的起點

* @param high 陣列的終點

*/public

void

quicksortnonrecusion

(int

a,int low ,

int high)

if(par < high-1)

while

(top!=-1

)if(par < high-1)

}}

我們也可以畫張圖來理解一下這個非遞迴的演算法:

這是剛開始初始化的情況

然後我們依次的將棧頂的兩個元素作為新的high和新的low,再次求解partition,直到棧空為止,這樣就實現了快速排序的非遞迴演算法了。

對於遞迴的快速排序我們可以很容易的寫出他的遞迴表示式,在最好的情況下,每次劃分所取的基準都恰好為中值,每次劃分都產生大小兩個為n/2的區域,此時的t(n)為:

t (n

)=o(1) \qquad n<1\\ 2t(\frac)+o(n) \qquad n\ge 1 \end\right.

t(n)= )

o(nlog​n

) 上面我們僅僅是分析了對於快速排序最好的情況,對於最壞的情況下發生劃分過程產生的兩個區域分別包含n-1個元素和1個元素的時候,由於函式partition的時間複雜度為o(n

)o(n)

o(n)

,所以如果演算法中partition的每一步都出現這種不對稱劃分,則其計算時間複雜度的t(n)為:

t (n

)=o(1) \qquad n<1\\ t(n-1)+o(n) \qquad n\ge 1 \end\right.

t(n)=)

o(n2)

由此我們可以基本判斷在平均時間複雜度為o(n

log⁡n)

o(n\log_{} )

o(nlog​n

),但是在不同的序列中還是可以會出現不對稱的劃分,所以我們該怎麼解決這個問題呢?

其實我們發現導致快速排序不好的因為他的劃分不對稱,那如何才能將劃分對稱呢?關鍵點就是我們基準數的選擇上,如果我們每一次都選擇的是整個待排序列中中間位置的數。那麼對於快排來說,每一次都是對稱的劃分。下面介紹三種基準劃分的方法:

這個其實我們已經很熟悉了,因為上面寫的所有**都是利用這乙個來進行劃分的,也就是我們選擇待排序列的頭乙個元素或者是最後乙個元素。

對於上面的取法我們無法解決掉劃分的問題,那麼我們可以採用隨機找基準的辦法,但是在隨機情況下未知性太高了。

由於隨機基準選取的隨機性,使得他並不能適應所有的情況,有可能同乙個序列會出現不同的情況,目前最好的方法就是三數取中法。他的做法是:選擇陣列開頭,中間和最後三個元素,通過比較,選擇中間的值作為快排的基準值

public

intnumberofthree

(int

a,int low,

int high)

if(a[low]

> a[high])if

(a[mid]

> a[low]

)return a[low]

;}

ifdef cplusplus深度剖析

時常在cpp的 之中看到這樣的 ifdef cplusplus extern c endif 這樣的 到底是什麼意思呢?首先,cplusplus是cpp中的自定義巨集,那麼定義了這個巨集的話表示這是一段cpp的 也就是說,上面的 的含義是 如果這是一段cpp的 那麼加入extern c 處理其中的 ...

深度剖析ostream

雖然c 學了大半個月了,自己學了解了很多關於c 的知識,大的概念懂了不少,但是還是想說想要徹底的弄懂c 絕非易事。當別人問到乙個很小的知識點些許你都要愣很久,事實告訴我們不能太好高騖遠。為什麼要寫ostream而不是istream,因為當我們自定義型別 student類 的時候,如果需要輸出 stu...

MapReduce 深度剖析

mapreduce 深度剖析 在hadoop2.x大行其道的年代,其優秀的資源管理框架 系統 高可用的分布式儲存系統,備受企業青睞。然因上述之惑,往往不能盡得其中之深意。此篇部落格筆者為大家一一解惑。雖說我們能夠編寫乙個完成mapreduce程式,並執行它。然其執行的細節,我們卻未必清楚,往往初學者...