快排的思想:如果要排序數組中下標從 p 到 r 之間的一組數據,我們選擇 p 到 r 之間的任意一個數據作爲 pivot(分區點)。
我們遍歷 p 到 r 之間的數據,將小於 pivot 的放到左邊,將大於 pivot 的放到右邊,將 pivot放到中間。經過這一步驟之後,數組 p 到 r 之間的數據就被分成了三個部分,前面 p 到 q-1之間都是小於 pivot 的,中間是 pivot,後面的 q+1 到 r 之間是大於 pivot 的。
根據分治、遞歸的處理思想,我們可以用遞歸排序下標從 p 到 q-1 之間的數據和下標從 q+1 到r 之間的數據,直到區間縮小爲 1,就說明所有的數據都有序了。
如果我們用遞推公式來將上面的過程寫出來的話,就是這樣:
遞推公式:
quick_sort(p…r) = quick_sort(p…q-1) + quick_sort(q+1, r)
終止條件:
p >= r
- 快速排序也可以很快解決top k問題
- 和歸併排序相比解決了空間複雜度的問題,原地排序
- 是不穩定排序,原因是因爲分區的過程涉及交換操作,i所在的位置第一個不符合標準的元素,後面接着還有不符合標準的元素,當我們再往後發現了j是符合標準的元素,此時就要交換i和j位置。此時原先在前的元素就跑到後面了。所以說是不穩定排序算法。
- 時間複雜度是O(nlogn),極端情況下可能退化成O(n)。
public class SortQuick {
public static void main(String[] args) {
int a[] = {9, 8, 5, 6, 3, 4, 1, 2};
new SortQuick().quicksort(a, 0, a.length - 1);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + ", ");
}
System.out.println();
new SortQuick().selectTopK(a, 3);
for (int i = 0; i < 3; i++) {
System.out.print(a[i] + ", ");
}
}
public void selectTopK(int[] nums, int k) {
int partition = partition(nums, 0, nums.length - 1);
while (partition != k) {
if (partition > k) {
//說明還需要進一步縮小空間獲取前k大
partition = partition(nums, 0, partition - 1);
} else {
//說明還需要擴大空間獲取前k大
partition = partition(nums, partition + 1, nums.length - 1);
}
}
}
/**
* 這裏是遞歸,遞歸都要使用遞歸公式來思考代碼的編寫
*
* @param nums
* @param start
* @param end
*/
public void quicksort(int[] nums, int start, int end) {
if (start >= end) {
return;
}
int partition = partition(nums, start, end);
quicksort(nums, start, partition - 1);
quicksort(nums, partition + 1, end);
}
public int partition(int[] nums, int start, int end) {
//選取最後一個作爲基點,比他小的放前面,比他大的放後面
int point = end;
int index = start;//從start到index的值比較小的
for (int i = start; i <= end; i++) {
if (nums[i] < nums[point]) {
//如果當前i是滿足條件的,就把它放到index上
int temp = nums[index];
nums[index] = nums[i];
nums[i] = temp;
index++;
}
}
//把基準數挪到角標這裏
int temp = nums[index];
nums[index] = nums[point];
nums[point] = temp;
return index;
}
}