寫在前邊數據庫
咱們有這麼一個需求,老闆和咱們說,要求咱們作這麼一個員工系統,公司員工的相關信息和爲公司的貢獻值都會在這個系統進行記錄,每到月底評功輪賞的時候,根據員工這一個月的表現進行獎罰。你可能會說,這還很差作嗎?增刪改查,而後直接按照貢獻值從大到小排序就行了。數組
彆着急,還有一個需求就是公司每月都會進行抽獎福利,抽獎的方式是,老闆隨機抽取貢獻值爲第 K 大的貢獻值的員工送出福利一份,共選取 n 位,而不是評功論賞了,若是讓你實現一個系統,你該如何實現呢?框架
若是你學完今天的快速排序,就很輕鬆的解決老闆給你分配的任務啦。ide
思惟導圖函數
1性能
什麼是快速排序?大數據
顧名思義,快速排序,那確定快呀,那到底有多快呢?快不過三秒?動畫
假如咱們已經接過老闆在數據庫給咱們取出的本月每一個員工的信息,咱們單獨篩選出貢獻值,以下數據。3d
爲了可以更加清晰的講解,咱們對一些用到的特殊數據進行標識一下。blog
如上數據,咱們從 p 到 q 隨機找一個元素做爲區分點(pivot),什麼是區分點?稍後咱們解釋。咱們就選擇最後一個數據 5 吧,而後咱們以 5 爲區分點,而後從 p 開始遍歷元素,若是當前遍歷的元素小於 5,咱們就放在 5 的前邊,若是當前遍歷的元素大於 5,咱們就放在 5 的後邊,最後的結果以下:
看了上邊的一頓操做,咱們也明白了爲何 5 是區分點了。上邊的數據也沒有從小到大呀?彆着急,重點來了。
咱們是總體數據按照 5 爲區分點進行從新排列數據的,若是咱們使用一樣的方式分別對 5 左邊和 5 右邊的數據分別進行這種方式的劃分,直到劃分到區間爲 1 爲止,是否是數據就會變的有序了?沒錯,這就是咱們所說的快速排序。
有小夥伴會問到,這多麻煩,也快不過三秒呀?咱們後邊會有性能分析的,到時候就知道快排比咱們以前講的冒泡、插入有多快了。
2
動畫實現
3
快速排序的原理
雖然咱們上邊籠統的分析了快速排序的基本過程,可是其中有兩個中要的知識點,快速排序的過程用到了遞歸和分治思想,咱們分開進行分開講解。
一、
遞歸
首先看一下快速排序的遞推公式,咱們不斷的將大區間分割成小區間,而後對小區間再次進行分割。
咱們能夠總結出以上的遞推公式。
由於咱們不斷的將大區間分紅小區間,而後一直分下去,不對,一直分總有一個盡頭的,因此這也是遞歸的終止條件。當知足這個條件時,就再也不繼續往下進行遞歸,那麼快速排序的遞歸條件是什麼呢?上邊也說到了,當區間只剩一個數據的時候,咱們再也不進行劃分,因此遞歸條件爲:
p >= q
遞歸的代碼實現:
二、
分治思想
咱們之因此將大問題不斷的分紅小問題,就是用到的分治思想,分而治之,將分解的小問題解決了,大問題天然而然就會獲得解決。
最關鍵的是快速排序中有一個分區函數 partition,分區函數的做用就是隨機找到一個區分點 pivot,而後對數據進行分區,該函數會返回分區後 pivot 的下標。
咱們好奇的是如何進行分區的?咱們須要用到一個分區函數 partition,咱們想到最簡單的方法可能就是小於 pivot 的元素放到數組 a 中,大於 pivot 的元素放到數組 b 中,而後合併 a 和 b,完成分區。
若是咱們不考慮空間上的消耗的話,這樣寫沒毛病的。可是,爲了考慮到空間上的消耗,也就是咱們但願空間複雜度是 O(1),不得不讓分區函數佔用少的內存空間,咱們須要在原數組中完成分區,而不是另外開闢新的空間。
這個過程咱們單純的想是很難想出來的,並且很是有技巧性,因此咱們一塊兒來看一下。咱們仍是以上邊的數據爲例,從 p 開始遍歷元素,分別和 pivot 區分點元素進行比較,若是小於區分點元素,咱們就進行交換,若是大於區分點元素,咱們就不進行交換,咱們具體來看一下動畫的實現。
4
快速排序的性能
咱們知道快速排序的整個實現過程了,下面咱們來分析一下快速排序的性能如何,不是你說很快嘛?能快過三秒嗎?
時間複雜度
咱們先來看時間複雜度,快速排序時間複雜度的計算是分區操做的時間加上合併的時間,快速的時間複雜度爲 O(nlogn)。這是理想狀況下,爲何呢?由於咱們隨機選擇區分點不可能每次都能將數據一分爲二。
還有一種極端的狀況就是,若是原數據是一組有序數據,若是每次都要選擇最後一個元素爲區分點,大約須要進行 n 次操做,每次遍歷 n/2 個元素,因此時間複雜度就會推化成 O(n²)。
雖然存在這種狀況,可是這種狀況的機率是極低的,並且咱們有方法能夠將這種方法降到最低,在基礎環節,咱們很少囉嗦。快速排序大部分狀況下的平均時間複雜度爲 O(nlogn)。
空間複雜度
咱們上邊也特別強調了,咱們分區函數只需在原數組中進行分區操做就能夠完成,不須要開闢額外的內存空間,因此空間複雜度爲 O(1)。
快速排序不管是時間效率仍是空間效率,足以比咱們以前講的冒泡排序和插入排序要效率高的多,在一些排序函數的框架源碼中,咱們也會使用到快速排序,因此快排的應用仍是很是普遍的,所謂快不過三秒「真男人」。
5
代碼實現
JavaScript 版本
Java 版本
6
小結
咱們回到文章開頭的問題上,咱們有一組員工的貢獻值數據,咱們要隨機選取第 K 大的貢獻值的員工發放獎品,如何實現呢?
你可能會問,今天講的快速排序和這個問題有什麼直接的掛鉤呢?表面看起來並無什麼掛鉤,而這個問題的解決是對快速排序代碼的一個變體,稍微改動一下,就能夠輕鬆解決上述問題。
好比幾位員工的貢獻值以下:七、九、四、三、六、二、5 。第 4 大元素就是 5,那就恭喜貢獻值爲 5 的員工得到獎金一份,雖然實際狀況下不太可能用這種方式發獎品,這裏咱們只是拿這個例子來說。
咱們將上邊的數據像快速排序同樣分爲三部分,分別爲 [0,p-1] p [p,q],這是已經完成分區函數的數據,由於咱們從 0 開始的,而後判斷當前的 p + 1 是否等於 K?若是等於 K ,那麼數組中下標爲 p 的元素就是第 K 大數據。
圖片
如上圖的 5 就是第四大數據,可是它在數組中的下標爲 3,因此須要加 1。