Sorting Algorithms:Quick Sort
前言
該部落格用於本弱雞複習鞏固,打牢基礎,還望各大佬不吝賜教。
基本思路
1.對一個未排序序列,假設從該序列中的元素中取一個基準值pivotkey,將小於pivotkey放左邊,大於pivotkey放右邊;
2.接著以該k為中間,左右兩邊的分割作為新的序列,重新進行1操作。
快排因為用到了遞迴操作,所以在簡單排序中效能不如直接插入排序,
而在大量資料排序時,遞迴產生的效能影響對於演算法的整體效能優勢可以忽略。
動圖示例
演算法複雜度分析
平均 | 最壞 | 最好 | 穩定性 | 空間複雜度 |
---|---|---|---|---|
O(nlogn) | O(n^2) | O(nlogn) | 不穩定 | O(logn) |
p.s.
- 最壞情況:待排序為正序或逆序,這樣每次分割後的子序列一個之比上一次序列少一個元素,一個為空。如 1 2 3 4 5 pivotkey=1;分割後一個序列為 2 3 4 5 一個為空,最終O(n^2)
- 最好情況:每一次分割都能平分,很均勻 O(nlogn)
- 平均情況:O(n*logn) 數學歸納法
- 空間複雜度:主要由遞迴而產生的對棧空間的影響
- 最好:O(logn)
- 最壞:O(n)
- 平均:O(logn)
- 穩定性 不穩定 比較和交換是跳躍進行的
程式碼實現
import java.util.Arrays;
import java.util.Random;
/**
* QuickSort
* 1.對一個未排序序列,假設從該序列中的元素中取一個基準值pivotkey,將<pivotkey放左邊 >pivotkey放右邊
* 2.接著以該k為中間,左右兩邊的分割作為新的序列,重新進行1操作
* <p>
* 快排因為用到了遞迴操作,所以在簡單排序中效能不如直接插入排序
* 而在大量資料排序時,遞迴產生的效能影響對於演算法的整體效能優勢可以忽略
* <p>
* 時間複雜度分析:
* 最壞情況:待排序為正序或逆序,這樣每次分割後的子序列一個之比上一次序列少一個元素,一個為空
* 如 1 2 3 4 5 pivotkey=1;分割後一個序列為 2 3 4 5 一個為空
* 最終O(n^2)
* 最好情況:每一次分割都能平分,很均勻 O(n*logn)
* 平均情況:O(n*logn) 數學歸納法
* 空間複雜度:主要有地櫃而產生的對棧空間的影響
* 最好:O(logn)
* 最壞:O(n)
* 平均:O(logn)
* 穩定性 不穩定 比較和交換是跳躍進行的
* <p>
* 如何合理基準值pivotkey
* 該值的取值對該演算法有相當影響,若pivotkey取到了最大或最小,都會增加演算法複雜度,影響效能
* 1.隨機選取,在待排序列中隨機選取,以降低取到最大或最小值的概率
* 2.三數取中,在待排序列的左端,中間,右端去三個值選取中位數,節省隨機數產生的時間開銷,以降低取到最大或最小值的概率
* 三數取中時,比較的同時應將三個元素按中間,小,大的順序重新排好位置
* 3.九數取中,三次取樣,每次取三個數,取它們的中位數,再取三個中位數的中位數
*/
public class QuickSort {
public static void main(String[] args) {
int[] a = new int[10];
boolean flag = true;
//random array
for (int i = 0; i < a.length; i++) {
Random rd = new Random();
a[i] = rd.nextInt(10);
}
System.out.println("Random Array :");
System.out.println(Arrays.toString(a));
System.out.println();
System.out.println("Quick Sort :");
//快速排序開始
quickSort(a, 0, a.length - 1);
System.out.println(Arrays.toString(a));
}
/**
* @param a
* @param low
* @param high
*/
public static void quickSort(int[] a, int low, int high) {
//該值定義了從哪個位置開始分割序列
int pivot;
//當high-low大於某一值時適合快速排序
//if ((high - low) >MAX_LENGTH_INSERT_SORT) 該值取7或50
if (low < high) {
//partition方法對序列進行排序
pivot = partition(a, low, high);
//分割兩個序列繼續進行快排操作
quickSort(a, low, pivot - 1);
quickSort(a, pivot + 1, high);
}
}
/**
* @param a
* @param low
* @param high
* @return
*/
public static int partition(int[] a, int low, int high) {
//取每個序列的第一個值作為基準值
int pivotkey = a[low];
while (low < high) {
//從序列的右邊開始往左遍歷,直到找到小於基準值的元素
while (high > low && a[high] >= pivotkey) {
high--;
}
//將元素直接賦予給左邊第一個,即pivotkey所在的位置
a[low] = a[high];
//a[high] = pivotkey;
//從序列的左邊邊開始往右遍歷,直到找到大於基準值的元素
while (high > low && a[low] <= pivotkey) {
low++;
}
//此時的a[high]<pivotkey,已經被賦予到左邊,所以可以將元素賦予給a[high]
a[high] = a[low];
//a[low] = pivotkey;
}
//最後的low是基準值所在的位置
a[low] = pivotkey;
return low;
}
}
複製程式碼
如何合理取基準值pivotkey
- 該值的取值對該演算法有相當影響,若pivotkey取到了最大或最小,都會增加演算法複雜度,影響效能。
- 隨機選取,在待排序列中隨機選取,以降低取到最大或最小值的概率。
- 三數取中,在待排序列的左端,中間,右端去三個值選取中位數,節省隨機數產生的時間開銷,以降低取到最大或最小值的概率。
三數取中時,比較的同時應將三個元素按中間,小,大的順序重新排好位置。 - 九數取中,三次取樣,每次取三個數,取它們的中位數,再取三個中位數的中位數。
參考
GeeksforGeeks:https://www.geeksforgeeks.org/quick-sort/
十大經典排序演算法:https://www.cnblogs.com/onepixel/articles/7674659.html