顾名思义,快速排序是实践中的一种快速排序算法,在C++或对Java基础类型的排序中特别有用。它的平均运行时间是O(NlogN);但最坏情形性能为O(N2)。我会先介绍快速排序过程,再讨论如何优化。html
采用分治法,将数组分为两部分,并递归调用。将数组S排序的快排过程java
将枢纽元与数组最后一个元素调换,使枢纽元离开要被分割的数据段;算法
初始化两个索引left和right,分别指向数组第一个与倒数第二个元素;数组
若是left索引指向的元素小于枢纽元,则left++;不然,left中止。right索引指向的元素大于枢纽元,right--;不然,right中止。安全
若是left<right,则交换两个元素,循环继续3,4步骤;不然跳出循环,将left对应的元素与枢纽元交换(这时候完成了分割)。递归调用这两个子序列。app
假设全部元素互异(即都不相等)。下面会说重复元素怎么处理。性能
接下来要作的就是将小于枢纽元的元素移到数组左边,大于枢纽元的元素移到数组右边。优化
当left在right的左边时,咱们将left右移,移过那些小于枢纽元的元素,并将right左移,移过那些大于枢纽元的元素。当left和right中止时,left指向一个大于枢纽元的元素,right指向一个小于枢纽元的元素,若是left<right,则将这两个元素交换。这样是将一个大于枢纽元的元素推向右边而把小于枢纽元的元素推向左边。咱们来图示过程:left不动,而right左移一个位置,以下图:ui
咱们交换left与right指向的元素,重复这个过程,直到left>right。spa
至此,咱们能够看到,left左边的元素都小于枢纽元,右边的元素都大于枢纽元。咱们继续递归左右序列,最终可完成排序。
上面咱们假设的是元素互异,下面咱们讨论重复元素的处理状况。
在大规模输入量中,重复元素仍是挺多的。考虑能将这些重复元素进行有效排序,仍是很重要。
快速排序真的快吗?其实也不必定,对于小数组(N<=20)的输入序列,快速排序不如插入排序;而且在咱们上面的优化中,采用三数中值分割时,递归获得的结果能够是只有一个,或者两个元素,这时会有错误。因此,继续优化是将小的序列用插入排序代替,这会减小大约15%的运行时间。较好的截止范围是10(其实5-20产生的效果差很少)。
对于三数中值分割还能够进行优化:假设输入序列为a,则选择a[left],a[center],a[right],选择出枢纽值,并将最小,与最大值分别放到a[left],a[right],将枢纽值放到a[right-1]处,这样放置也是正确的位置,而且能够防止right向右进行比较时不会越界;这样左右起始位置就是left+1,right-2。
public class Quicksort {
/**
* 截止范围
*/
private static final int CUTOFF = 10;
public static void main(String[] args) {
Integer[] a = {8, 1, 4, 9, 6, 3, 5, 2, 7, 0};
System.out.println("快速排序前:" + Arrays.toString(a));
quicksort(a);
System.out.println("快速排序后:" + Arrays.toString(a));
}
public static <T extends Comparable<? super T>> void quicksort(T[] a) {
quicksort(a, 0, a.length - 1);
}
private static <T extends Comparable<? super T>> void quicksort(T[] a, int left, int right) {
if (left + CUTOFF <= right) {
//三数中值分割法获取枢纽元
T pivot = median3(a, left, right);
// 开始分割序列
int i = left, j = right - 1;
for (; ; ) {
while (a[++i].compareTo(pivot) < 0) {
}
while (a[--j].compareTo(pivot) > 0) {
}
if (i < j) {
swapReferences(a, i, j);
} else {
break;
}
}
//将枢纽元与位置i的元素交换位置
swapReferences(a, i, right - 1);
//排序小于枢纽元的序列
quicksort(a, left, i - 1);
//排序大于枢纽元的序列
quicksort(a, i + 1, right);
} else {
//插入排序
insertionSort(a, left, right);
}
}
private static <T extends Comparable<? super T>> T median3(T[] a, int left, int right) {
int center = (left + right) / 2;
if (a[center].compareTo(a[left]) < 0) {
swapReferences(a, left, center);
}
if (a[right].compareTo(a[left]) < 0) {
swapReferences(a, left, right);
}
if (a[right].compareTo(a[center]) < 0) {
swapReferences(a, center, right);
}
// 将枢纽元放置到right-1位置
swapReferences(a, center, right - 1);
return a[right - 1];
}
public static <T> void swapReferences(T[] a, int index1, int index2) {
T tmp = a[index1];
a[index1] = a[index2];
a[index2] = tmp;
}
private static <T extends Comparable<? super T>> void insertionSort(T[] a, int left, int right) {
for (int p = left + 1; p <= right; p++) {
T tmp = a[p];
int j;
for (j = p; j > left && tmp.compareTo(a[j - 1]) < 0; j--) {
a[j] = a[j - 1];
}
a[j] = tmp;
}
}
}
//输出结果
//快速排序前:[8, 1, 4, 9, 6, 3, 5, 2, 7, 0]
//快速排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
本篇从如何较好选择枢纽元,分析重复元素的处理及递归分红小数组时更换为插入排序三个方面进行快速排序的优化,系统全面详述了快速排序原理、过程及其优化。快速排序以平均时间O(NlogN)进行,是java中基础类型使用的排序算法。能够去看一下Arrays.sort方法。到这里,我就要回过头去完善求解topK问题了,能够利用快速排序的思想,达到平均O(N)求解topK。
以为能够的小伙伴们点个推荐或小赞支持啊。