快速排序
終於到我們人見人愛,花見花開,鳥見鳥呆,車見車爆胎
快排的好處不用多說,平均時間的nlogn
,o(1)
的輔助空間,一般比其他的排序演算法要快得多。
當然也有些不足,首先,不穩定,所以多關鍵字排序的最後一排肯定不能用了,另外,最壞情況下則為n^2
。快速排序和歸併排序一樣,都屬於分治的排序方法。據說當年hoare
首次提出該演算法時,驚訝於對快排的效能及**的優美,最後還是狠狠地進行了完全的數學分析後,才發表快排**的。
所有的計算機科學家、程式設計師排列出心中的十大演算法時,快速排序總是會位列其中。可想而知快排的實用性加優美性。
和歸併一樣,它將陣列劃分成兩個部分,不同在於,歸併分別把兩個部分進行處理好了,再進行merge
。而快排則是在劃分時就進行處理,再分別對兩個部分進行排序。
劃分過程中,我們會重排陣列(使用swap
交換),得到這樣的乙個陣列:
1、我們選定陣列中某個元素作為pivot
,劃分後,該元素出現在i位。
2、a[1]-a[i-1]中的元素,都比
a[i]
小。3、a[i+1]-a[n]中的元素,都比
a[i]
大。所以,在quicksort
中,我們需要乙個呼叫乙個用於劃分的
partition
函式,在判斷該部分陣列元素個數
>1
後,則劃分,再遞迴處理劃分後的兩個部分。**如下:
void quicksort(int a, int l, int r)
}
下面是劃分的處理了。劃分才是快排中最重要的,就像歸併裡的merge
一樣。而劃分就我所知的,就有兩種不同的實現方法,乙個用
for,從頭掃到尾,乙個用
while
,兩頭往中間走。
本質上兩個方法肯定沒有區別,一般用的是while
的方法,但是,
for的**更顯優美,所以兩個都介紹下吧。
首先是流行的while
。這裡先不介紹隨機化的快排,我們直接選取該部分的第乙個元素作為
pivot
,也就是把
a[l]
從原位置中取出來,放到
pivot
,a[l]
已經為空。另外還知道最高位和最低位所處的位置
l 和
r,就可以開始比較劃分過程。
此時我們把l 和 r
看作是已經劃分好的元素的分界線,
l 以左一定小於
pivot,r
以右一定大於
pivot
,而且由於
c語言函式中形參的改變不影響實參,沒有必要使用額外的變數(給變數起名是件很痛苦的事)。
下面就看你的愛好是從頭到腳還是反過來,我這裡是從r到l
,將當前
a[r]
與pivot(
即初始的
a[l])
比較,若大於(等於看你心情),則符合要求,
r--,並繼續用
a[r]
與pivot
比較;否則,結束該迴圈,將
a[r]
的值(因為
),放到
a[l]
的位置中——
a[l]
原本是空的,則
a[r]
為空。
while (a[r] >= pivot) r--;
a[l] = a[r];
接著又從l到r
,相似的方法,比較後,或
l++,或
a[l]
的值放到
a[r]
。過程如圖:
如此迴圈下去,什麼時候結束呢?l和r
鵲橋相會時,此時,
l == r,l
左邊的項全小於
pivot,r
右邊的項全大於
pivot
,我們也就找到了
pivot
在劃分後的位置。
另外,我剛才專門試了一下,必須給上面的兩個while的迴圈條件加上 l < r,否則會出界。
於是,完整的partition while
版**為:
吐槽啊,這個編輯器偶爾給我抽一下的……int partition(int a, int l, int r)
a[l] = pivot;
return l;
}
然後是for
版了。這回我改一改風格,先上**:
int partition(int a, int l, int h)
swap(l, i - 1);
return i - 1;
}
這個寫法最開始是從《程式設計實踐》裡面看來了(相近,但有改動),然後在上
coursera
的演算法分析與設計時,用
while
版快排連錯兩次以後,一看,原來是要這個寫法,印象瞬間深刻了。
相對不是那麼好理解,就像我之前面試時,就遇到不熟練的問題。現在我就來解釋清楚吧!
看圖說話最方便,所以,來張圖:
這裡的分法和while
不同,while
是中間未分,這裡是末尾未劃分。除了用個
pivot = a[l]
(圖中是
a[r]
)以外,還需要迴圈的
j,和表示
<=pivot
和 >pivot
的分界線的
i(也有叫
last
的)。
從l+1
開始到r,用i
來表示第乙個
>pivot
的數的位置(原圖是最後乙個
<= pivot
的位置),所以,每個數
a[j]
都和pivot
比較,大於
pivot
的,自然是在
i的右邊,而小於
pivot
的話,就
a[j]
和a[i]
進行交換,再將
i右移一位。
全部走一遍之後,我們已經得到劃分好的兩塊。不過,pivot
還在a[l]
處不動呢,於是又和
a[i-1]
交換下,即完成整個劃分過程。
本來還打算上《程式設計實踐》裡的快排**的,看看沒太多差別,就算了。
JAVA演算法系列 快速排序
首先說一下什麼是快排,比冒泡效率要高,快排的基本思路是首先找到乙個基準元素,比如陣列中最左邊的那個位置,作為基準元素key,之後在最左邊和最右邊設立兩個哨兵,i和j 之後,開始按住左哨兵 i 讓右哨兵 j 往左走 j 找到比key小的元素後,按住右哨兵 j 開始讓左哨兵往右走 i 直到找到比key大...
演算法系列 排序演算法(四)快速排序
快速排序是通過兩個指標相互交換完成一次快速排序,類似於遞迴的二分排序,從交換上來講比較像冒泡 為什麼這麼說呢?不管是插入還是直接,都需要在移動之前遍歷元素 冒泡直接比較交換。公升序 資料 4251 376下標 0123 4561 指標1 4 指標2 6 指標1是用來二分的標準,指標2是被二分的元素,...
演算法系列 JavaScript快速排序思想實現
快速排序離不開遞迴的思想,你如果不了解遞迴,可以結合我另外一篇文章來學習 演算法入門之遞迴分而治之思想的實現 網上有有趣的動態圖來表示快速排序,但其實我們大部分程式設計師都是腦子不太好使那種,即使看了形象生動的動態圖,還是想不到具體實現思路。排序都有基本的步驟,快排也不例外,通常分為這麼幾步 1 選...