优先介绍时间复杂度为 $O(log(N))$ 的算法,其余 $O(n^2)$ 的直接复制粘贴在底部。
public class Note2_1_QuickSort {
// 快速排序方法
public static void quickSort(int[] q, int l, int r) {
if (l >= r) return;
// TODO 1 注意是 l - 1 和 r + 1 让下文从第一个数就开始比较
int i = l - 1, j = r + 1;
int x = q[(l + r) >> 1];
while (i < j) {
// TODO 2 不要弄反顺序
do i++; while (q[i] < x);
do j--; while (q[j] > x);
if(i < j) swap(q, i, j);
}
// TODO 3 以 j 为基准
quickSort(q, l, j);
quickSort(q, j + 1, r);
}
// 交换数组中的两个元素
private static void swap(int[] q, int i, int j) {
int temp = q[i];
q[i] = q[j];
q[j] = temp;
}
public static void main(String[] args) {
int[] arr = {1, -1, 3, 2};
quickSort(arr,0, arr.length - 1);
for (int item : arr) {
System.out.println("item = " + item);
}
}
}
24.1.15更:这里会出现两个问题
while(q[i]<x);while(q[j]>x);
为什么不能改为 while(q[i]<=x);while(q[j]>=x);
?问题在于如果我们的基准x刚好取到最大/最小值,就会发生指针越界。
如数组4 2 1 5 3
, 第一轮迭代时 x = 1, q[i] = 4,q[j] = 1 但如果把等号加上,q[j] = 1 时就不会停下来,一直往左走直到越界。
quick_sort(q, l, j); quick_sort(q, j + 1, r);
改成 quick_sort(q, l, i); quick_sort(q, i + 1, r);
因为 l + r >> 1
是向下取整 , 如果 | l - j | = 1
, 那么 l + j >> 1 = l
,那么递归到最后 j = l
就是
quick_sort(q , l , l)
就结束了, 不会死循环,如果用 i 做边界,x的取值还是q[l + r >> 1],
那最后 | i - l | = 1
, quick_sort(q , l , i)
执行完了 l 和 i 的值还没变,还是quick_sort(q , l , i),就死循环了。
换种说法就是,每次快排,数据范围都得改变(缩小)直到 缩到一个数,避免缩小到两个数的时候 ,数据范围不变,一直是那两个数,所以就得选用合适的边界:
所以到最后我们只需要记住两件事: