前言
排序算法很是多,幾乎每一個人學的第一個排序算法都是冒泡算法,可是冒泡算法的時間複雜度是很高的,是一種效率很低的算法。而目前來講,快速排序是相對比較好的一種算法:實現難度低,時間複雜度低。但快速排序在一些狀況下也可能出現退化到和冒泡算法同樣的時間複雜度,因此須要讀者注意一下,下面我會講到。那麼接下來就來看看這個算法。java
筆者才疏學淺,有不一樣觀點歡迎評論區或私信討論。如需轉載請留言告知。
另外歡迎閱讀筆者的我的博客一隻修仙的猿的我的博客,更精美的UI,擁有更好的閱讀體驗。git
算法思路
遞歸算法的思路其實很簡單:算法
- 從數據中取出一個數,即爲mid
- 比mid小的數放在左邊,比mid大的數放在右邊
- 最後把mid放在中間
- 對左右兩邊的子數組進行遞歸排序,直到只剩下一個元素則所有排序完成。
具體的實現思路咱們舉一個例子:數組
如今有一個數組:dom
![數組](http://static.javashuo.com/static/loading.gif)
這裏細心的讀者會發現誒第三個數怎麼後面有一點,這個不是手滑打出來的。而是爲了證實在排序的過程當中,兩個3的順序是否會發生顛倒,從得出這是不是一個穩定的排序。spa
-
首先咱們取出一個數,當成哨兵。這裏咱們取第一個數
5
當成哨兵。而後咱們把這個數和最後一個數交換。爲何須要交換呢?這樣咱們就不須要去額外設置一個變量存儲哨兵了。如圖:code -
接下來咱們設置兩個變量:
min
和max
們分別表示比mid小的數的最大的下標,和比mid大的數最小的座標。排序舉個例子以下圖:遞歸
-
接下來咱們從左邊開始,若是
min
指向的數比哨兵小,則min = min+1
,不然則停下來。執行完以後以下圖:接口 -
而後從右邊開始,若是
max
指向的數大於等於哨兵,則max = max-1
,不然則停下來,執行完成以後以下圖: -
而後把min和max指向的數字進行交換:(這裏能夠看到兩個3的順序發生了顛倒,因此這是一個不穩定的排序)
-
重複3,4,5步驟直到
min==max
-
把下標爲
min
的數字和哨兵進行交換,至此一輪的排序已經完成: -
對先後的子數組進行遞歸排序。完成排序。
快速排序的核心就是利用遞歸分治的思路來下降時間複雜度。若是咱們每次都恰好選到中位數,那麼遞歸樹的高度就是logn
(這裏的n表明元素的個數),每一層遞歸都須要遍歷一次數組,那麼時間複雜度最好的狀況就是:
O(logn)
可是,若是每次都取到最小或者最大的數,那麼快排的遞歸樹高度則爲n,那麼他的時間複雜度將退化爲:
O(n^2)
因爲只須要常量空間,因此空間複雜度爲:
O(1)
代碼示範
下面使用java語言作一個規範。接口參數爲:整型數組,數組的開始下標,數組的結束下標。(因多是子數組因此須要下標參數)
private void fastSort(int[] nums,int start,int end) { // 終止條件:start>=end if(start>=end) return; // 記錄原始的下標 int startRow = start; int endRow = end; // 採用隨機數獲取下標,能夠下降退化到n^2的機率 int random = new Random().nextInt(end-start)+start; // 交換兩個數 swap(nums,random,end); // 重複上述的3,4,5步驟 while(start<end){ // 記得這裏的每一步都必須判斷start<end while( nums[start]<nums[endRow] && start<end ){ start++; } while( nums[end]>=nums[endRow] && start<end ){ end--; } // 若是相同則把哨兵放到中間,排序結束 // 不然start和end交換位置,繼續循環 if(start==end) swap(nums,start,endRow); else{ swap(nums,start,end); } } // 最後對子數組進行遞歸排序 paixu(nums,startRow,start-1); paixu(nums,start+1,endRow); } // 交換數組中的兩個數 private void swap(int[] nums,int one,int two){ int temp = nums[one]; nums[one] = nums[two]; nums[two] = temp; }