快排本质是分治算法,其大致流程是如下:
public void sort(int[] arr, int l, int r) {
if (l >= r) return;//递归终止判定
int mid = parSort(arr, l, r);//对mid数进行排序
sort(arr, l, mid - 1);//左递归
sort(arr, mid + 1, r);//右递归
}
大框架是这样的,难点在于如何将目标数组分成两个。
很多教程里面讲到,这个函数需要把数组分成一边大于某数x,另一边小于x。
其实漏掉了一个重要的东西,就是x的位置在分完之后代表着什么,有什么意义?
其实很简单,把比x小的数都放到x的左边,把x数大的数都放到x的右边,此时x在数组的下标为i,这个i其实和数组排序后x所在的位置是一样的。也就是说这个分组操作之后,就将x的位置排好了。而且也将数组更加的有序化了。
至于如何分组,可以使用一种朴素的实现方式,那就是直接创建两个数组a与b,用以存储大于等于x的数和小于x的数。至于x,不放入数组中,或者放入a中最末尾。最后将两个数组中的数覆盖原始的arr[l,r]。
代码见下:
public void sort(int[] arr) {
sort(arr, 0, arr.length - 1);
}
/*快排利用到了递归,递归要做的工作是把关键数两边的数安排好*/
public void sort(int[] arr, int l, int r) {
if (l >= r) {
return;
}
int mid = parSort(arr, l, r);
sort(arr, l, mid - 1);
sort(arr, mid + 1, r);
}
private int parSort(int[] arr, int l, int r) {
int mid = l + r >> 1;
int com = arr[mid];
int[] tmp1 = new int[r - l + 1];
int[] tmp2 = new int[r - l + 1];
int cnt1 = 0;
int cnt2 = 0;
for (int i = l; i < r + 1; i++) {
if (i != mid) {//比较数不放进去
if (arr[i] <= com) {
tmp1[cnt1] = arr[i];
cnt1++;
} else {
tmp2[cnt2] = arr[i];
cnt2++;
}
}
}
int start = l;
for (int i = 0; i < cnt1; i++) {
arr[start] = tmp1[i];
start++;
}
arr[start] = com;
int res = start;
start++;
if (cnt2 > 0) {
for (int i = 0; i < cnt2; i++) {
arr[start] = tmp2[i];
start++;
}
}
return res;
}