經典快排的思路是選取陣列的最後乙個數 x,按照問題一的思路把整個陣列劃分成小於等於 x | 大於 x
兩個部分,將 x 和 大於 x 部分陣列的第乙個元素交換位置。此時整個陣列劃分成小於等於 x | x | 大於 x
三個部分,也就是這一次排序將 x 值排好位置。
再分別對小於等於 x
和大於 x
中的陣列遞迴劃分,直到劃分成乙個數,此時所有元素也完成排序。
按照問題二的思路可以對經典快排做改進,使得每次劃分陣列成為小於 x | 等於 x | 大於 x
三個部分,通過這種排序方式可以一次劃分多個 x 值的位置,排序效率得到提高。
但是,經典快排出現問題與資料狀況有關。每次選擇 x 值都是陣列的最後乙個數,如果遇到[1,2,3,4,5]
或者[5,4,3,2,1]
這種陣列,演算法時間複雜度將變成 o(n^2)。
隨機快排是經典快排的一種改進,通過生成隨機下標 i,選擇 a[i] 和最後乙個數 x 進行交換,再使用經典快排。此時的事件就是乙個概率事件,需要使用期望來估計演算法的時間複雜度。
仍以[1,2,3,4,5]
為例,經過隨機快排初始變換,可以形成下列五種情況,資料狀況的影響有效降低。在長期期望下,隨機快排演算法的時間複雜度為 o(n*logn)。由於每次劃分資料都需要記錄 =x 陣列的下標範圍,因此額外的空間複雜度為 o(logn)。
5,2
,3,4
,1;1
,5,3
,4,2
;1,2
,5,4
,3;1
,2,3
,5,4
;1,2
,3,4
,5.
思想
隨機快排和經典快排的差別就在於新增了一行**,使比較的數 x 具有隨機性。
通過這種隨機的方法處理特殊的資料,使得演算法具有更好的魯棒性。
單邊迴圈法和雙邊迴圈法
上述快排的實現都是從乙個方向上遍歷元素,然後分成兩個陣列,成為單邊迴圈法。還有另一種實現的方法是雙邊迴圈,在《大話資料結構》書中可以看到實現。
雖然實現方法多種多樣,但是其核心本質仍然是選取陣列中的值與陣列其他元素比較大小,經過一輪迴圈之後將陣列分成兩個部分。單邊迴圈和雙邊迴圈本質上是一樣的,只是實現方式上不同。
快排**
思路:使用 quicksort() 函式處理陣列,先進行隨機處理,使用核心方法 partition() 將資料分成小於 x | 等於 x | 大於 x
三個部分,返回等於區域的左右下標值 [a, b],遞迴呼叫小於 x
區域和大於 x
區域。
quicksort
(arr, left, right)
partition()
;//就是荷蘭國旗問題的過程
public
static
void
quicksort
(int
arr)
quicksort
(arr,
0, arr.length -1)
;}public
static
void
quicksort
(int
arr,
int left,
int right)
if(left < right)
}public
static
int[
]partition
(int
arr,
int left,
int right)
else
if(arr[left]
== arr[right]
)else
}return
newint
;//return new int;
}
手寫**過程中發現有些判斷多餘,因此做了注釋。在整體**中,有些語句能夠精簡成更簡潔的**,但是為了思路的連貫性,僅將簡潔的**做注釋保留。從這些精簡的**中也可以看到一些優雅的**真令人驚嘆。 快排2 經典快排和荷蘭國旗快排
基礎知識見 建議先閱讀基礎知識,並自己手推一遍 演算法原理 第一步 取陣列最後乙個數作為num,將陣列中的 num的數放在陣列的左邊,num的數放在陣列的右邊,這是可以理解為分成了兩個陣列 第二步 然後將 num的部分當成乙個陣列,繼續第一步 num的部分同理 第三步 若陣列的大小 2,則結束。流程...
排序演算法 快排
今天聊聊排序演算法,排序演算法平時也會用到,有很多比如,冒泡,快排,選擇,歸併排序等 今天就聊一聊快速排序演算法排序演算法的乙個宗旨就是經過一趟排序,何為一趟排序呢?就是遍歷完一次陣列,陣列中的一部分資料比另外一部分的資料都要小 當然這兩部分資料內部仍然是無序的,然後我們再對這兩部分資料分別進行排序...
利用荷蘭國旗問題改進經典快排和隨機快排
每次取陣列中最後乙個值,依照這個值把陣列分為兩份,小於的在左邊,大於的在右邊。再依次按照這樣的思想進行操作。ps 荷蘭國旗問題可以看另一篇部落格 荷蘭國旗問題把陣列是分為三個部分的,小於 等於 大於這三個部分。按照這樣的思想,等於部分就不需要進行再次進行排序,這樣就能減少很大一部分的開銷。改進之後的...