1. 程式人生 > >快速排序的基本原理

快速排序的基本原理

當前 兩種 之前 種類型 相等 等待 漸進 解析 右移

前言:
今天淩晨在臨睡前隨意翻看了 @啊哈磊 的坐在馬桶上看算法:快速排序,對快速排序算法有了新的認識。在此之前,我只對冒泡排序算法有一些了解。
在此感謝 @啊哈磊 的耐心講解。
已知一個一維數組arr,包含17個元素:

int[] arr = new int[] { 13, 25, 6, 84, 71, 63, 96, 49, 7, 52, 30, 28, 1, 74, 93, 69, 40 };

我們從中選擇任意一個數作為基準,例如25。
另外,聲明兩個指針變量a和b,將他們置於數組的兩端,即13和40。
左側指針a負責找比基準值大的數,它從左向右移動;
右側指針b負責找比基準值小的數,它從右向左移動。
當它們其中一個找到合適的數時,則在此停留並等待另一個指針找到合適的數。如果另一個指針也找到了合適的數,那麽就令它們交換得到的數(指針b先開始移動)。

第一次的停留結果:
84,1
OK,將這兩個數交換位置。這就變成了:
(1)13, 25, 6, 1, 71, 63, 96, 49, 7, 52, 30, 28, 84, 74, 93, 69, 40
a b
此時,指針a停留位置的數值是1,指針b停留位置的數值是84。
第二次停留結果:
71,7
再將這兩個數交換位置:
(2)13, 25, 6, 1, 7, 63, 96, 49, 71, 52, 30, 28, 84, 74, 93, 69, 40
a b
此時,指針a停留位置的數值是7,指針b停留位置的數值是71。
這時我們會發現,7與71之間的數都比25大。這意味著,如果指針b繼續向左搜尋比25小的數值,必將與指針a相遇。即:指針a與b都處在7的位置。
OK,將指針a和b共同停留的7與基準數25交換:
(3)13, 7, 6, 1, 25, 63, 96, 49, 71, 52, 30, 28, 84, 74, 93, 69, 40
ab
這時我們會發現:基準數25左側數列中的每個數都比25小,而右側數列中的每個數都比25大。這樣,我們就能將這個數組以基準值25為界限,劃分為兩個數列:
13, 7, 6, 1
63, 96, 49, 71, 52, 30, 28, 84, 74, 93, 69, 40
而在這兩個數列中,第一個數列的左側指針停留的值和第二個數列的右側指針停留的值分別與與數組的左右側指針一致。
到這裏,可能有些人會有疑問:如果在第(2)步之後,率先移動的是指針a而不是指針b,那麽是否可以呢?
我可以很明確地對你說——完全可以!
至於為什麽,我們接下來繼續解析:
假如我們率先移動的是指針a而不是指針b,那麽在指針a率先移動並停留於大於基準數的數值63後,指針b在向左尋找小於基準數25的數值時,將與指針a相遇。即:
(4)13, 25, 6, 1, 7, 63, 96, 49, 71, 52, 30, 28, 84, 74, 93, 69, 40
ab
我們會觀察到:63左側的所有數都不大於25,而63及右側的所有數都不小於25。
同樣,我們可以將數組(4)分為兩個數列:
13, 25, 6, 1, 7
63, 96, 49, 71, 52, 30, 28, 84, 74, 93, 69, 40
而在數列(3)中,我們會發現(4)的結論同樣使用於(3):
13, 7, 6, 1, 25, 63, 96, 49, 71, 52, 30, 28, 84, 74, 93, 69, 40
ab
結論與(4)類似:25左側的所有數都不大於25,而25及右側的所有數都不小於25。
只不過在(3)中,我們得出的結論是:25左側的所有數都小於25,而右側的所有數都大於25。
根據上面的推導過程,我們很容易再將這兩種類型的子數列依照新的基準數進行排序。
其實快速排序與冒泡排序的最大不同之處在於,後者的排序是漸進式(每次比較的兩個數都相鄰),而前者的排序是跳躍式。當預排序的數組足夠長且比較次數一定時,涉及更多新的數值的跳躍式無疑具有漸進式不可比擬的優點。當然,前者與後者在最壞情況下的比較次數是相等的。
不過,不要忘了:快速排序的基準必須是當前排序數列的最大值與最小值之間的一個數。否則,很容易發生StackOverflowException。

快速排序的基本原理