前言:
快速排序和归并排序其实是差不多的,只不过就是归并不管数组内容是什么,直接一分为二,
而快排是选择一个元素,将其放在合适的位置,使左边的元素小于它,右边的大于它~这样逐渐递归~
它的核心就是Partition过程:
- 通常使用数组的第一个元素来作为分界的标志点(基点),记为l(left)
- 之后逐渐遍历右边所有未被访问元素
- 在遍历的过程中逐渐整理让整个数组左部分小于 v 这个元素值,右部分大于 v。
- 在此过程中,用j 来记录左右部分的分界点,当前访问的元素记为 i 。这样整个数组中 arr[l+1……j ] < v,arr[j+1……i-1] >v
- 最后将l上的v和j交换,这样就切分成功了~
代码实现:
private static int partition(Comparable[] arr, int l, int r){
Comparable v = arr[l];
int j = l; // arr[l+1...j] < v ; arr[j+1...i) >
for(int i = l + 1; i <= r; i ++){
if(arr[i].compareTo(v) < 0){
j ++;
SortTestHelper.swap(arr, j, i);
}
}
SortTestHelper.swap(arr, l, j);
return j;
}
private static void sort(Comparable[] arr, int l, int r){
if(l >= r) return;
int p = partition(arr, l, r);
sort(arr, l, p-1);
sort(arr, p+1, r);
}
public static void sort(Comparable[] arr){
int n = arr.length;
sort(arr, 0, n-1);
}
代码优化
(1) 优化一
还是递归到底的问题,高级的排序算法在底层时可以使用插入排序来优化快排,当元素较少时,可使用插入排序来提高性能~
(2) 优化二
提到过归并和快排都是分成两部分,归并是平分为二,整个层数就是logn层,每一层都是消耗O(n)时间
但是快速划分的是选取的那个标志点、这样就会分成一大一小,假如选的最小或最大的那个点,那么情况更严重,即整个数组几乎有序~这样划分树的高度就是n, 每层消耗O(n),最终复杂度就是O(n^2)。
解决的办法就是:随机选取一个标记点~,但是这还是会出现最坏情况仍是O(n^2),只是说概率很低。
总结:
归并和快速尽管都是O(nlogn)级别的,但是快排是有常数级别的优势,即使已经对归并优化过了。