* <第4章> 快速排序 </第4章>
* 4.1 分而治之(D&C策略)
* (1)找出基线条件,这种条件必须尽可能简单
* (2)不断将问题分解(或者说缩小规模),直到符合基线条件
* 4.2 快速排序 示例:{@link QuickSort}
* (1)选择基准值
* (2)将数组分成两个子数组:小于基准值的元素和大于基准值的元素
* (3)对这两个子数组进行快速排序:递归
* (4)归纳证明:对于快速排序,可使用类似的推理。在基线条件中,我证明这种算法对空数组或包含一个
* 元素的数组(不用排序)管用。在归纳条件中,我证明如果快速排序对包含一个元素的数组管用,对包含两
* 个元素的数组也将管用;如果它对包含两个元素的数组管用,对包含三个元素的数组也将管用,以此类推。因此,我可以说,快速排序对任何长度的数组都管用。
* (5)快速排序的独特之处在于,其速度取决于选择的基准值
* 4.3 再谈大O表示法
* (1)在平均情况下,快速排序的运行时间为O(nlog(n))
* (2)在最糟情况下,其运行时间为O(n²)
* 4.3.1 比较合并排序和快速排序
* (1)快速查找的常量比合并查找小,因此如果它们的运行时间都为O(nlog(n)),快速查找的速度将更快
* (2)大O表示法中的n实际上是这样的:c*n,c是算法所需的固定时间量,被称为常量
* (3)通常不考虑这个常量,因为如果两种算法的大O运行时间不同,这种常量将无关紧要
* 4.3.2 平均情况和最糟情况
* (1)快速排序的性能高度依赖于你选择的基准值
* (2)总是以第一个元素为基准值,且是有序的,那是最糟情况,时间复杂度为O(n²),如果以中间元素为基准值,那是最佳情况(平均情况),时间复杂度为O(nlog(n))
* 4.4 小结
* (1)D&C将问题逐步分解。使用D&C处理列表时,基线条件很可能是空数组或只包含一个元素的数组
* (2)实现快速排序时,请随机地选择用作基准值的元素。快速排序的平均运行时间为O(nlog(n))
* (3)大O表示法中的常量有时候事关重大,这就是快速排序比合并排序快的原因所在
* (4)比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时,O(log n)的速度比O(n)快得多
*
/**
* 快速排序
*/
public class QuickSort {
/**
* 拼接数组和基准值
*
* @param a1
* @param p
* @param a2
* @return
*/
private static int[] concatArr(int[] a1, int p, int[] a2) {
int[] arr = new int[a1.length + 1 + a2.length];
System.arraycopy(a1, 0, arr, 0, a1.length);
arr[a1.length] = p;
System.arraycopy(a2, 0, arr, a1.length + 1, a2.length);
return arr;
}
private static int[] quickSort(int[] arr) {
if (arr.length < 2) {
// 基线条件:数组元素为空,或只包含一个元素,不需要排序,直接返回
return arr;
} else {
// 递归条件
// 基线值:每次以中间元素作为基准值是最佳的选择
int pivot = arr[arr.length / 2];
int[] less = new int[arr.length];
int j = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] <= pivot && i != arr.length / 2) {
less[j] = arr[i];
j++;
}
}
// 小于基线值的数组元素a1
less = Arrays.copyOf(less, j);
int[] greater = new int[arr.length];
j = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] > pivot && i != arr.length / 2) {
greater[j] = arr[i];
j++;
}
}
//大于基线值的数据元素a2
greater = Arrays.copyOf(greater, j);
// 拼接a1+pivot+a2,将两部分数组分别继续快速排序
return concatArr(quickSort(less), pivot, quickSort(greater));
}
}
public static void main(String[] args) {
int[] arr = new int[2000000];
Random random = new Random(47);
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(100000);
}
long start = System.currentTimeMillis();
quickSort(arr);
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + " ms");
start = System.currentTimeMillis();
Arrays.sort(arr);
end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + " ms");
}
}