快速排序是一種二分的排序演算法,這種演算法的誕生來自於對有序陣列的觀察。我們假設有以下陣列:
1,2,3,4,5,6,7,8,9
這是乙個已經按照從小到大排序完畢的有序陣列。觀察以上陣列,取其中間數5,我們可以發現5以左的數1,2,3,4均比5小,5以右的數6,7,8,9均比5大。我們以5為分界線將陣列分為兩部分:
1,2,3,4
6,7,8,9
分別取中間數3和8,我們可以得到同樣的結論:左邊的數總小於中間數,右邊的數總大於中間數。由此可見,乙個有序數列的每乙個二分子數列總滿足中間數以左的數小於中間數,中間數以右的數大於中間數。
快速排序的基本思想就是:
使完整陣列滿足a
aa段所有數小於b
bb段所有數,但a段與b段內部不一定有序。 aaab
bb3,2,1,4
7,9,6,8
使a
aa段內a
1a_1
a1段所有數小於a
2a_2
a2段所有數,b
bb段內b
1b_1
b1段所有數小於b
2b_2
b2段所有數,但每一小段內不一定有序。 a
1a_1
a1a
2a_2
a2b
1b_1
b1b
2b_2
b21,2
3,47,6
9,8以此類推二分陣列,直到每個陣列內都只有乙個元素時,整個陣列便變得有序了。
可見快速排序是乙個典型的二分演算法,且可以使用遞迴的方式實現。
我們以剛才舉例的陣列的逆序列為例:
9,8,7,6,5,4,3,2,1
假如我們採用單向迭代的方法來完成快速排序,取中間數為9,之後掃瞄後面8個數字。由於8個數字均小於9,我們只需要將9插到隊伍的最後。
8,7,6,5,4,3,2,1,9
再取中間數為8,掃瞄後面8個數字,發現只需要把8插到9的前面就行。以此類推,在資料規模為n
nn的情況下,需要n−1
n-1n−
1次插入。
但是,這是在我們事先已經知道有成塊的子串中所有的數均小於中間數的情況下得出的結論。實際情況下,在我們考慮單獨的數時,這個數小於中間數並不意味著下乙個數小於中間數。所以我們每次選取中間數後需要維護兩個序列,乙個儲存比中間數小的數,乙個儲存比中間數大的數。每次掃瞄數,都要與中間數比較後,加入兩個序列中的乙個當中。每一次選取中間數,複製數都需要n−1
n-1n−
1次操作,總共選取n
nn個中間數的話,這趟快速排序的時間複雜度就是o(n
2)
o(n^2)
o(n2
)。因此,我們必須保證所有資料位置的變換都在原陣列內部進行。
我們又知道,假如有一排積木緊密排在一起,要將其中的一塊移到另一塊積木處,就必須得把原來在該位置的積木拿開。
令積木大小不同,如果我們要實現積木從左到右由小到大的排序,可以想象我們一開始將最左邊積木拿開,再從右往左找,如果見到了比拿開的積木更小的積木,就拿到最左邊的空位。這時右邊就空出來了乙個積木的空位,我們再從左往右找,找到一塊比拿開的積木大的積木,放到右邊的空位……
直到空位出現在隊伍中間時,左邊的積木,儘管還不是從小到大排列的,但至少都拿開的積木,也小於右邊的積木。我們再把拿開的積木放回空位,就完成了第一次排序。接著再對右邊的積木和左邊的積木重複同樣的操作,最終整個陣列就會變成有序的。
拿開的積木就是我們的基準數,積木的大小就是陣列中數字的大小,上面積木排序的過程其實就是陣列快速排序的過程。這樣的操作時間複雜度是多少呢?由於我們二分地選擇基準數,基準數的選擇是log
nlogn
logn
次,考慮最壞的情況,每一段數列中除了基準數外的數中,有一半的數需要移動,對於完整數列即n−1
2\frac
2n−1
次,隨著數列二分,資料規模以1
2\frac
21為公比縮小,根據等比數列求和公式,得:單邊移
動次數=
n−12
⋅(1−
12lo
gn)1
−1
2單邊移動次數=\frac ·(1-\frac^)}}
單邊移動次數
=1−2
12n
−1⋅
(1−2
1lo
gn)
lim n
→+
∞\lim n→+∞
limn→+
∞時,單邊總共移動次數為n−1
n-1n−
1,加上我們需要選擇log
nlogn
logn
次基準數,總的時間複雜度就是o(n
logn
)o(nlogn)
o(nlog
n)。
static
void
quicksort
(int s,
int e)
e--;}
while
(s < e)
s++;}
} numbers[s]
= mid;
quicksort
(start, s -1)
;quicksort
(s +
1, end)
;}
演算法學習 1 快速排序
序 快速排序比較難以理解,多baidu下,多想想最後就明白了,以下是自己的理解 有興趣的可以參考 來學習演算法 有兩種實現模式 1 找到乙個交換乙個 左右側 如下 void quicksort qsort wk int s,int lindex,int rindex if lindex rindex...
演算法學習(1) 快速排序
課程參考 資料結構 浙江大學 10.1.1 演算法概述 10.1.2 選主元 10.1.3 子集劃分 10.1.4 演算法實現 演算法視覺化 史上最容易理解的 十大經典演算法 動態圖展示 網上有的 看了下都比較長,陳越姥姥給的演算法思路較清晰,量少,遂記錄之。快速排序的思想是首先選取乙個主元,然後將...
演算法學習筆記 快速排序
在學習左神的演算法課,對快速排序有了更多的理解,在此記錄,以備後查 描述 快排1.0是基於單個支點進行遞迴操作。大體來講,是選擇陣列arr中的某乙個數作為支點pivot,經過一通操作 一般叫partition 使得左邊的數均小於等於pivot,右邊的數均 pivot,如此一來,pivot就待在了自已...