快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。由C. A. R. Hoare在1962年提出。
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
实现方法:
快速排序一般基于递归实现。其思路是这样的:
1.选定一个合适的值(理想情况中值最好,但实现中一般使用数组第一个值),称为“枢轴”(pivot)。
2.基于这个值,将数组分为两部分,较小的分在左边,较大的分在右边。
3.可以肯定,如此一轮下来,这个枢轴的位置一定在最终位置上。
4.对两个子数组分别重复上述过程,直到每个数组只有一个元素。
5.排序完成。
示意图:
[图片上传失败...(image-9b9105-1528258808854)]
很多实际的项目中使用了快排算法。但通常对算法都进行了调整(tuning),比如Java.util.Arrays类中的sort函数就使用了快排算法,但使用了双参考值(Dual-Pivot Quicksort)等一些改进措施。由于快排算法为递归算法,可以用循环代替递归函数调用,改进性能。
代码地址如下:
https://github.com/zhxhcoder/codeProj
具体简单实现,代码如下
public static void main(String[] args) {
int[] arr = {0, 0, 0, 4, 5, 1, 5, 1, 5, 1, 5, 1};
quickSort(arr, 0, arr.length - 1);
for (int a : arr) {
System.out.println(a);
}
}
private static void quickSort(int[] arr, int low, int high) {
if (low == high || low == (high - 1)) return;
if (low < high) {
int pivot = partition(arr, low, high); //将数组分为两部分
quickSort(arr, low, pivot - 1); //递归排序左子数组
quickSort(arr, pivot + 1, high); //递归排序右子数组
}
}
private static int partition(int[] arr, int low, int high) {
int pivot = arr[low]; //枢轴记录
while (low < high) {
while (low < high && arr[high] >= pivot) --high;
arr[low] = arr[high]; //交换比枢轴小的记录到左端
while (low < high && arr[low] <= pivot) ++low;
arr[high] = arr[low]; //交换比枢轴小的记录到右端
}
//扫描完成,枢轴到位
arr[low] = pivot;
//返回的是枢轴的位置
return low;
}
以下代码是对上面的封装
//对上述快排函数原型修改,使其可以对任意对象类型数组进行排序。这个函数为内部使用,外部排序函数接口为sort(),sort函数要求对象必须实现Comparable接口,可以提供编译时类型检测,见后文。
public static void main(String[] args) {
int[] arr = {0, 0, 0, 4, 5, 1, 5, 1, 5, 1, 5, 1};
quickSort(arr, 0, arr.length - 1);
for (int a : arr) {
System.out.println(a);
}
}
private static void quickSort(int[] arr, int begin, int end) {
if (begin == end || begin == (end - 1)) return;
if (begin < end) {
int pivot = partition(arr, begin, end); //将数组分为两部分
quickSort(arr, begin, pivot - 1); //递归排序左子数组
quickSort(arr, pivot + 1, end); //递归排序右子数组
}
}
private static int partition(int[] arr, int begin, int end) {
int pivot = arr[begin]; //枢轴记录
while (begin < end) {
while (begin < end && arr[end] >= pivot) --end;
arr[begin] = arr[end]; //交换比枢轴小的记录到左端
while (begin < end && arr[begin] <= pivot) ++begin;
arr[end] = arr[begin]; //交换比枢轴小的记录到右端
}
//扫描完成,枢轴到位
arr[begin] = pivot;
//返回的是枢轴的位置
return begin;
}
//使用泛型,对任意对象数组排序,该对象类型数组必须实现Comparable接口
public static > void sort(T[] input) {
quickSort(input, 0, input.length);
}
//添加对List对象进行排序的功能,参考了Java中的Java.util.Collections类的sort()函数
public static > void sort(List list) {
Object[] t = list.toArray();//将列表转换为数组
quickSort(t, 0, t.length); //对数组进行排序
//数组排序完成后再写回到列表中
ListIterator i = list.listIterator();
for (int j = 0; j < t.length; j++) {
i.next();
i.set((T) t[j]);
}
}
//由于Java中原始数据类型(int、double、byte等)无法使用泛型,所以只能使用函数重载机制实现对这些原始类型数组(int[]、double[]、byte[]等)的排序。这里为了共用同一个排序函数,利用原始类型的(AutoBoxing,UnBoxing)机制将其封装为对应对象类型,组成新的对象数组,排序后再解封装,这样的缺点是需要额外的转换步骤、额外的空间保存封装后的数组。另一种方式是将排序代码复制到各个重载函数中,官方API中的Java.util.Arrays这个类中的sort()函数就是使用这种方法,可以从Arrays类的源代码看出。
public static void sort(int[] input) {
Integer[] t = new Integer[input.length];
for (int i = 0; i < input.length; i++) {
t[i] = input[i];//封装
}
quickSort(t, 0, t.length);//排序
for (int i = 0; i < input.length; i++) {
input[i] = t[i];//解封装
}
}
//double[]数组的重载函数
public static void sort(double[] input) {
Double[] t = new Double[input.length];
for (int i = 0; i < input.length; i++) {
t[i] = input[i];
}
quickSort(t, 0, t.length);
for (int i = 0; i < input.length; i++) {
input[i] = t[i];
}
}
//byte[]数组的重载函数
public static void sort(byte[] input) {
Byte[] t = new Byte[input.length];
for (int i = 0; i < input.length; i++) {
t[i] = input[i];
}
quickSort(t, 0, t.length);
for (int i = 0; i < input.length; i++) {
input[i] = t[i];
}
}
//short[]数组的重载函数
public static void sort(short[] input) {
Short[] t = new Short[input.length];
for (int i = 0; i < input.length; i++) {
t[i] = input[i];
}
quickSort(t, 0, t.length);
for (int i = 0; i < input.length; i++) {
input[i] = t[i];
}
}
//char[]数组的重载函数
public static void sort(char[] input) {
Character[] t = new Character[input.length];
for (int i = 0; i < input.length; i++) {
t[i] = input[i];
}
quickSort(t, 0, t.length);
for (int i = 0; i < input.length; i++) {
input[i] = t[i];
}
}
//float[]数组的重载函数
public static void sort(float[] input) {
Float[] t = new Float[input.length];
for (int i = 0; i < input.length; i++) {
t[i] = input[i];
}
quickSort(t, 0, t.length);
for (int i = 0; i < input.length; i++) {
input[i] = t[i];
}
}
//测试用的main函数
public static void main(String[] args) {
//生产一个随机数组成的int[]数组,用来测试
int LEN = 10;
int[] input = new int[LEN];
Random r = new Random();
System.out.print("int[] before sorting: ");
for (int i = 0; i < input.length; i++) {
input[i] = r.nextInt(10 * LEN);
System.out.print(input[i] + " ");
}
System.out.println();
System.out.print("int[] after sorting: ");
sort(input);
for (int i : input) {
System.out.print(i + " ");
}
System.out.println();
//生成一个字符串数组,用来测试
String[] s = new String[]{"b", "a", "e", "d", "f", "c"};
System.out.print("String[] before sorting: ");
for (int i = 0; i < s.length; i++) {
System.out.print(s[i] + " ");
}
System.out.println();
System.out.print("String[] after sorting: ");
sort(s);
for (int i = 0; i < s.length; i++) {
System.out.print(s[i] + " ");
}
System.out.println();
//生成一个字符串列表,用来测试
List l = new LinkedList();
s = new String[]{"b", "a", "e", "d", "f", "c"};
System.out.print("LinkedList before sorting: ");
for (int j = 0; j < s.length; j++) {
l.add(s[j]);
System.out.print(s[j] + " ");
}
System.out.println();
sort(l);
System.out.print("LinkedList after sorting: ");
for (String ts : l) {
System.out.print(ts + " ");
}
System.out.println();
}