排序算法之快速排序
上一篇我们有介绍归并排序,我们知道归并排序是利用分治思想解决的,其实快速排序也是利用分治思想解决的,但是实现思路上却又完全不同了。快速排序的思想是,从数组中找到一个点记为pivot,把小于pivot的放到他的左边,把大于pivot的放到他的右边。
核心思想
分治思想的代码实现是可以用递归去做的,这里我们推导一下递归公式,显然,根据分治思想,要把这个数组一直往下分,同时更换数据位置,直到无法再分的时候,数组自然就有序了。
公式:quick_sort(p...r)=quick(p...q-1)+quick(q+1...r);
终止条件:p>=r
代码实现
package com.test;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = new int[]{3, 3, 2, 5, 3, 4, 1, 7, 6, 9};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
static void quickSort(int[] arr, int p, int r) {
if (p >= r) {
return;
}
int q = partition(arr, p, r);
quickSort(arr, p, q - 1);
quickSort(arr, q + 1, r);
}
/**
* @param arr, p, r
* @return: int
* @Title:
* @Description: 选择一个pivot点, 进行分区, 我们把数组视为已处理区和未处理区
* @date: 21:06 2021/3/22 0022
* Modification History:
* Date Author Description
* -------------------------------------------*
* 21:06 2021/3/22 0022 xx.huang 修改原因
*/
static int partition(int[] arr, int p, int r) {
//分区点的值
int pivot = arr[r];
//i标记已处理区的最后一个元素
int i = p;
//如果arr[j] < pivot,那么就把arr[j]放到已处理区的最后
for (int j = p; j <= r - 1; j++) {
if (arr[j] < pivot) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
}
}
int temp = arr[r];
arr[r] = arr[i];
arr[i] = temp;
return i;
}
}
我们重点分析partition
,partition
方法的主要作用是找出分区点pivot
,我们一般把数组最后一个数字视为pivot
,然后我们处理的时候把数组视为已处理区和未处理区,已处理区的所有数字都是小于pivot
的,我们用i
来记录已处理区的结尾,用j
来记录未处理区的开头,一旦未处理区的数字小于pivot
,我们就把这个数字放到已处理区的结尾,当遍历完数组以后,此时应当把pivot
添加到已处理区的结尾,这样一来pivot
左边的数字就小于它,右边的数字也大于它了。然后根据递归思想,接下来就是把pivot
左边的数组和右边的数组再如此反复处理,这样最后的数组就是有序的了。例如{3,5,1,4,9},在经过第一次处理的时候,因为我们选定pivot
为9,所以数组没有变化,此时我们开始处理pivot
左边的数组,也就是{3,5,1,4},选定pivot
为4,经过处理,{3,1,5,4},此时4应当添加到已处理区后面,也就变成了{3,1,4,5},pivot
左边的数组为{3,1}处理后变成{1,3},右边只有一个数字所以不用处理,最后合并数组就是{1,3,4,5,9},这里所谓的视为已处理区和未处理区,实际上并没有把数组真的拆开,就和归并排序一样,并没有拆数组,都是在原数组上面进行的操作。
例题:https://leetcode-cn.com/problems/smallest-k-lcci/
这道题解法是利用快速排序过程中的特点,即每完成一次排序的时候,pivot
左边的数字都小于他,假设arr[k-1]=pivot
,那么pivot
就是倒数第k小的数字,我们想要获取最小的k个数,也就是当某次递归的排序中,出现q==k的情况,就可以终止递归,从数组取出前k个数字就是了。代码如下
package com.controller;
import java.util.Arrays;
public class LeetCode1714 {
public static void main(String[] args) {
int[] arr = new int[]{3, 3, 2, 5, 3, 4, 1, 7, 6, 9};
System.out.println(Arrays.toString(smallestK(arr, 3)));
}
public static int[] smallestK(int[] arr, int k) {
quickSort(arr, k, 0, arr.length - 1);
int[] srr = new int[k];
for (int i = 0; i < k; i++) {
srr[i] = arr[i];
}
return srr;
}
static void quickSort(int[] arr, int k, int p, int r) {
if (p >= r) {
return;
}
int q = partition(arr, p, r, k);
//此时终止递归,q前面的数字就是最小的k个数
if (q == k) {
return;
}
quickSort(arr, k, p, q - 1);
quickSort(arr, k, q + 1, r);
}
static int partition(int[] arr, int p, int r, int k) {
int pivot = arr[r];
int i = p;
for (int j = p; j <= r - 1; j++) {
if (arr[j] < pivot) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
}
}
int temp = arr[r];
arr[r] = arr[i];
arr[i] = temp;
return i;
}
}