前言:
快速排序和归并排序其实是差不多的,只不过就是归并不管数组内容是什么,直接一分为二,
而快排是选择一个元素,将其放在合适的位置,使左边的元素小于它,右边的大于它~这样逐渐递归~
它的核心就是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)级别的,但是快排是有常数级别的优势,即使已经对归并优化过了。