Sorting 排序演算法: Quick Sort 快速排序
Sorting 排序演算法: Quick Sort 快速排序
簡介
快速排序作為最常被應用的排序演算法,因為其擁有最好的平均時間效率,同時只用了常數的額外空間,所以受到大多數應用的青睞。
參考
正文
演算法思想原理
輸入
/* 傳入引數 */
int[] A // 原陣列(可以是任何型別的資料,這邊使用 int 型別為示例)
/* 限制 */
int n // 陣列長度
演算法思想
快速排序也和歸併排序一樣採用了分治的思想,但是與歸併不同的是劃分(partition)階段
並不只是單純的分成兩半,而是選定一個基準值(pivot)
,將整個陣列劃分成"比 pivot 小"和"比 pivot 大"的兩半,然後再分別排序。分類時的交換使得合併(merge)階段
只需要花費常數的時間代價即可完成
- 動圖
演算法流程
虛擬碼(參考:演算法導論)
Quick-Sort(A)
Quick-Sort(A, 0, A.length)
Quick-Sort(A, l, r)
if l < r
q = partition(A, l, r)
Quick-Sort(A, l, q - 1)
Quick-Sort(A, q + 1, r)
Partition(A, l, r)
pivot = A[r]
i = l - 1
for j = l to r - 1
if A[j] <= pivot
i = i + 1
exchange A[i] and A[j]
exchange A[i + 1] and A[r]
return i + 1
與歸併排序相同,分治演算法使用左右界作為引數,以 [0 … A.length - 1] 區間為起始。當子陣列長度大於 1 時(l < r),就進行劃分,然後再分別對兩個子陣列進行排序。
在劃分階段,直接選取最右邊的數作為基準(pivot)
,i 指向左子陣列(比 pivot 小)的末尾,j 遍歷一遍陣列,遇到比 pivot 小的數就交換到 i 位置。迴圈結束時將第 i + 1 個數也就是第一個大於 pivot 的值與 pivot 交換,就相當於劃分出以 i + 1
位置為界:i + 1 以左的都小於(等於) pivot,以右的都大於 pivot。如此遞迴知道陣列長度為 1。
演算法複雜度分析
先說結論:
- 時間複雜度:最壞 O(n2) / 平均 O(nlogn)
- 空間複雜度:O(1)
- 原址排序
- 不穩定的
- 時間複雜度:最壞 O(n2) / 平均 O(nlogn)
在最壞的情況下每次劃分都將所有元素劃分到同一邊,這時時間複雜度就接近 O(n2),然而這種情況是極少出現的,只要中間有幾次有分出兩個子陣列,漸進時間上還是趨近於 O(nlogn) 的,所以平均效率上是極好的
- 空間複雜度:1
在 partition 階段進行兩兩交換,沒有使用其他額外的空間,複雜度為 O(1)
- 原址排序
在原陣列內交換元素,使用區間作為排序界線
- 不穩定的
由於交換是小於等於 pivot 的一律向前交換,所以當 pivot 選取到存在重複的元素時會出現相對順序改變的問題,所以演算法是不穩定的
Java 實現
QuickSort.java
public class QuickSort {
public static void sort(int[] A) {
sort(A, 0, A.length - 1);
}
private static void sort(int[] A, int l, int r) {
if (l < r) {
int m = partition(A, l, r);
sort(A, l, m - 1);
sort(A, m + 1, r);
}
}
private static int partition(int[] A, int l, int r) {
int pivot = A[r];
int i = l - 1;
for (int j = l; j < r; j++) {
if(A[j] <= pivot) {
int tmp = A[++i];
A[i] = A[j];
A[j] = tmp;
}
}
A[r] = A[i + 1];
A[i + 1] = pivot;
return i + 1;
}
}
QuickSortTest.java
public class QuickSortTest {
@Test
public void test() {
int[] A = new int[]{1,3,5,7,9,2,4,6,8,0};
int[] ans = new int[]{0,1,2,3,4,5,6,7,8,9};
System.out.println("origin: " + Arrays.toString(A));
QuickSort.sort(A);
System.out.println("sorted: " + Arrays.toString(A));
assertArrayEquals(ans, A);
}
}
- 輸出
origin: [1, 3, 5, 7, 9, 2, 4, 6, 8, 0]
sorted: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
結語
作為擁有最好的比較排序代價,同時使用最少的額外空間(O(1))的比較排序演算法,快速排序是最常被呼叫的排序演算法之一。然而在基本款的快排之上,我們還可以在 pivot 的選取上進行優化,使得 partition 過後的子陣列更加平均以提升快速排序的平均效率。
相關文章
- 快速排序 (Quick Sort)排序UI
- 快速排序演算法(Quick_Sort)排序演算法UI
- 排序演算法之「快速排序(Quick Sort) _c++ 」排序演算法UIC++
- Array.sort 演算法原理(插入排序\快速排序in-place實現)演算法排序
- 排序演算法(3)插入排序(Insertion Sort)排序演算法
- 排序演算法之「歸併排序(Merge Sort)」排序演算法
- 排序演算法之「插入排序(Insertion Sort)」排序演算法
- 排序演算法__快速排序排序演算法
- 排序演算法:快速排序排序演算法
- 排序演算法 - 快速排序排序演算法
- 常見排序演算法及其實現(Binary,Insert、Select、Quick、Bubble.etc.Sort)排序演算法UI
- sort排序排序
- golang sort.Sort () 排序演算法學習Golang排序演算法
- Sort排序專題(5)快速排序(QuickSort)(C++實現)排序UIC++
- 排序演算法之 '快速排序'排序演算法
- 【JAVA演算法】排序演算法 -- 快速排序Java演算法排序
- Algorithm-sort 排序演算法 pythonGo排序演算法Python
- 演算法之常見排序演算法-氣泡排序、歸併排序、快速排序演算法排序
- 排序演算法 - 快速插入排序和希爾排序排序演算法
- 快速排序演算法排序演算法
- 【演算法】快速排序演算法排序
- 歸併排序演算法(merge_Sort)排序演算法
- selection_Sort(選擇排序演算法)排序演算法
- 排序演算法-Java實現快速排序演算法排序演算法Java
- 堆排序 Heap Sort排序
- Elasticsearch script sort 排序Elasticsearch排序
- Collections sort()排序方法排序
- 排序演算法之快速排序的實現排序演算法
- 畫江湖之演算法篇【排序演算法】快速排序演算法排序
- 畫江湖之演算法篇 [排序演算法] 快速排序演算法排序
- Arrays.Sort()中的那些排序演算法排序演算法
- 演算法之旅:快速排序演算法排序
- 演算法之快速排序演算法排序
- 看動畫學演算法之:排序-快速排序動畫演算法排序
- 排序:氣泡排序&快速排序排序
- python實現氣泡排序、插入排序以及快速排序演算法Python排序演算法
- 希爾排序(Shell Sort)排序
- 計數排序 - Counting Sort排序