快速排序
速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试,包括像腾讯,微软等知名IT公司都喜欢考这个,还有大大小的程序方面的考试如软考,考研中也常常出现快速排序的身影。
总的说来,要直接默写出快速排序还是有一定难度的,因为本人就自己的理解对快速排序作了下白话解释,希望对大家理解有帮助,达到快速排序,快速搞定。
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。
虽然快速排序称为分治法,但分治法这三个字显然无法很好的概括快速排序的全部步骤。因此我的对快速排序作了进一步的说明:挖坑填数+分治法:
import java.util.Arrays; /** * 第1次排序:[9, 6, 12, 34, 45, 79, 65] * 第2次排序:[6, 9, 12, 34, 45, 79, 65] * 第3次排序:[6, 9, 12, 34, 45, 79, 65] * 第4次排序:[6, 9, 12, 34, 45, 65, 79] */ public class QuickSort { public static int count = 0; public static void main(String[] args) { int array[] = {34, 65, 12, 45, 6, 79, 9}; quickSort(array); } public static void quickSort(int[] array) { if (array != null) { quickSort(array, 0, array.length - 1); } } private static void quickSort(int[] array, int beg, int end) { if (beg >= end || array == null) return; int p = partition(array, beg, end); // System.out.println("第一次排序"); System.out.print("第" + (++count) + "次排序:"); System.out.print(Arrays.toString(array)); System.out.println(); quickSort(array, beg, p - 1); quickSort(array, p + 1, end); } private static int partition(int[] array, int beg, int end) { int pivot = array[beg]; while (beg < end) { while (beg < end && array[end] >= pivot) end--; if (beg < end) array[beg++] = array[end]; while (beg < end && array[beg] <= pivot) beg++; if (beg < end) array[end--] = array[beg]; } array[beg] = pivot; return beg; } } // 快排空间复杂度O(log2(n)) 归并O(n) // // 心情不稳当,快些选一对好友来聊天-----------快速,希尔,简单选择,堆排序---------------不稳当 // // 快些以 nlog2(n)归队------------------------快速,希尔,归并,堆排序----------------------时间负载度 nlog2(n) // // // // 直接插容易插成O(n),起泡起的好编程O(n)------------------容易插,起的好,都是初始化序列已经有序
下面以中间为基排序
这个时候要注意,比较条件是大于或者小于,不能有等于,不然程序会出错
import java.util.Arrays;
import java.util.Comparator;
/**
* 快速排序。
* <p>
* 第1次排序:[34, 9, 12, 6, 45, 79, 65]
* 第2次排序:[6, 9, 12, 34, 45, 79, 65]
* 第3次排序:[6, 9, 12, 34, 45, 79, 65]
* 第4次排序:[6, 9, 12, 34, 45, 65, 79]
* 第5次排序:[6, 9, 12, 34, 45, 65, 79]
* 最终结果
* [6, 9, 12, 34, 45, 65, 79]
*/
/**
* 第1次排序:[34, 9, 12, 6, 45, 79, 65]
* 第2次排序:[6, 9, 12, 34, 45, 79, 65]
* 第3次排序:[6, 9, 12, 34, 45, 79, 65]
* 第4次排序:[6, 9, 12, 34, 45, 65, 79]
* 第5次排序:[6, 9, 12, 34, 45, 65, 79]
* 最终结果
* [6, 9, 12, 34, 45, 65, 79]
*/
/**
* 分治和递归,以中间为准基数,分割数组为两部分,
* 如果左边的index0指向数据小于准基数,则满足条件(左边的小于准基数),
* 下标增加一(index0++),用下一个数据和准基数对比,如果满足,再看一下个数据。
* 如果右边的数据大于准基数,则满足条件(右边的数据大于准基数),
* 下边减一(index1--),用上一个数据和准基数进行对比。。。
*/
public class QuickSort2 {
public static int count = 0;
public static void QuickSort2(int[] inputs, Comparator<Integer> cp) {
sort(inputs, 0, inputs.length - 1, cp);
}
/**
* 快速排序算法,结果是从小到大。
*
* @param inputs 待排序对象数组。
* @param low 首
* @param high 尾
*/
private static void sort(int[] inputs, int low, int high,
Comparator<Integer> cp) {
int index0 = low;
int index1 = high;
int mid;
if (high > low) {
mid = inputs[(low + high) / 2];
while (index0 <= index1) {
while ((index0 < high) && (cp.compare(inputs[index0], mid) < 0)) {
++index0;
}
while ((index1 > low) && (cp.compare(inputs[index1], mid) > 0)) {
--index1;
}
if (index0 <= index1) {
swap(inputs, index0, index1);
++index0;
--index1;
}
}
System.out.print("第" + (++count) + "次排序:");
System.out.print(Arrays.toString(inputs));
System.out.println();
if (low < index1) {
sort(inputs, low, index1, cp);
}
if (index0 < high) {
sort(inputs, index0, high, cp);
}
}
}
/**
* 交换。
*
* @param objs
* @param i
* @param j
*/
private static void swap(int[] objs, int i, int j) {
int T;
T = objs[i];
objs[i] = objs[j];
objs[j] = T;
}
public static void main(String[] args) {
int array[] = {34, 65, 12, 45, 6, 79, 9};
Comparator<Integer> integerComparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
};
QuickSort2(array, integerComparator);
System.out.println("最终结果");
System.out.println(Arrays.toString(array));
}
}
import java.util.Arrays; /** * 快速排序。 * 第1次排序:[34, 9, 12, 6, 45, 79, 65] * 第2次排序:[6, 9, 12, 34, 45, 79, 65] * 第3次排序:[6, 9, 12, 34, 45, 79, 65] * 第4次排序:[6, 9, 12, 34, 45, 65, 79] * 第5次排序:[6, 9, 12, 34, 45, 65, 79] * [6, 9, 12, 34, 45, 65, 79] */ public class QuickSort3 { public static int count = 0; public static void QuickSort3(int[] inputs) { sort(inputs, 0, inputs.length - 1); } /** * 快速排序算法,结果是从小到大。 * * @param inputs 待排序对象数组。 * @param low 首 * @param high 尾 */ private static void sort(int[] inputs, int low, int high) { int index0 = low; int index1 = high; int mid; if(count==3){ System.out.println(111); } if (high > low) { mid = inputs[(low + high) / 2]; while (index0 <= index1) { while ((index0 < high) && ((inputs[index0] - mid) < 0)) { ++index0; } while ((index1 > low) && ((inputs[index1] - mid) > 0)) { --index1; } if (index0 <= index1) { swap(inputs, index0, index1); ++index0; --index1; } } System.out.print("第" + (++count) + "次排序:"); System.out.print(Arrays.toString(inputs)); System.out.println(); if (low < index1) { sort(inputs, low, index1); } if (index0 < high) { sort(inputs, index0, high); } } } /** * 交换。 * * @param objs * @param i * @param j */ private static void swap(int[] objs, int i, int j) { int T; T = objs[i]; objs[i] = objs[j]; objs[j] = T; } public static void main(String[] args) { int array[] = {34, 65, 12, 45, 6, 79, 9}; QuickSort3(array); System.out.println(Arrays.toString(array)); } }
这个问题其实也没有多么复杂,但是网上这部分内容不多,故总结一下最优与最差比较次数。
n个元素线性表快速排序,最好情况下比较次数是多少?
参照严书的方法,以第一位作为标杆。
①考虑第一趟排序,无论怎样也要有n-1次比较次数;
②如果此时能把数据分成两部分,前一部分与后一部分元素个数相近,那样就是最优的。
例如,4 1 2 3 5 6 7,经过一趟排序,变成 1 2 3 4 5 6 7 。也就是说,以4为标杆,一趟后分成了两部分,这两部分个数相同或相差 1,那么快排会很优。
于是,可以想到,最优时,n个数,第一趟分成前⌊(n-1)/2⌋,后⌈(n-1)/2⌉两组,每组也是按照最优的分,设最优是an,于是有:
an = a⌊(n-1/2)⌋+a⌈(n-1/2)⌉ + n - 1
比如,n=7时,最优情况就是 a7=a3+a3+6 ,a3=a1+a1+2 ,而显然a1=0。
于是a3=2,a7=10。
举 例 4 1 3 2 6 5 7 。第一次4为标杆,一趟后为 2 1 34 6 5 7
第二趟两边分别以2,6为标杆,这样最好,因为是均分了。
问题,n=8时,最好排序次数?
a8=7+a4+a3,a3算的2,a4=3+a1+a2,显然a2=1,于是a4=4,故a8=13
因此,n=8时,最好情况下比较次数是13。
举例,4 1 3 2 6 5 7 8 可以数一下
对于别的情况,参照这个思路即可轻松求比较次数以及举例。
n个元素线性表快速排序,最坏情况下比较次数是多少?
这个就容易多了,因为顺序或者逆序时最坏的。
故比较次数,1+2+3……+ n-1 = n(n-1)/2