本文已参与「新人创作礼」活动,一起开启掘金创作之路。
思路
这个其实和荷兰国旗一样,都是先让一个数放在它该放的地方,然后就有两个部分出来了,然后利用递归解决就可以了。如果有不了解的可以点击这个链接去了解一下,帮助理解
举例
数组【5,4,9,0,2,3,7】
第一次先让5在应该在的地方
数组就变成了
【4,0,2,3,5,9,7】(这个样子不一定是对的,只是举例)
然后数组递归之后就变成了两个数组分别是
【4,0,2,3】
和
【9,7】
然后让他们再从最开始解决5的那样。
代码
我们先来看看我们是怎么利用荷兰国旗的问题解决5能找到自己的位置的
public static int[] netherlandsFlag(int[] arr, int L, int R) {
if (L > R) {
return new int[] { -1, -1 };
}
if (L == R) {
return new int[] { L, R };
}
int less = L - 1;
int more = R;
int index = L;
while (index < more) {
if (arr[index] == arr[R]) {
index++;
} else if (arr[index] < arr[R]) {
swap(arr, index++, ++less);
} else {
swap(arr, index, --more);
}
}
swap(arr, more, R);
return new int[] { less + 1, more };
}
这边返回的数组是相同区间的l和r。
那我们拿到相同区间的左右后,我们可以干什么呢?
我们可以把这些拍好的区域划开,然后在没有排好的区域里面执行操作
public static void process(int[] arr, int L, int R) {
if (L >= R) {
return;
}
swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
int[] equalArea = netherlandsFlag(arr, L, R);
process(arr, L, equalArea[0] - 1);
process(arr, equalArea[1] + 1, R);
}
用这个代码就可以不管排好的区域了。有人会问着变的随机数是什么意思
这个是为了避免偶然性,因为你固定一个位置之后,有可能数据环境不是很好,那就要递归好多次,就很危险,这个时候随机一个数,让所有交给上帝。
之后再用方法调用一下
public static void quickSort1(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
我们在之前的归并排序里面讲过,递归在生产环境里面是不受欢迎的,是因为它调用栈十分频繁,导致了效率的降低。那我们现在就实现一个非递归版本
在我们实现非递归版本之前,我们先要明白,为什么我们会用到递归,我们了解这个之后,我们看看我们是不是有东西可以替代这个递归。
我们这边创建递归的原因是不是为了排除排好的数字,然后在排好数字的左右各自调用了一个栈,那我们只要用一个变量记住我们现在应该调用哪一个部分,然后在要用的时候拿出来就好了。
那我们知道函数栈频繁的创建和销毁是很浪费的,那我们自己创建一个栈来使用是不是更好一点呢?
非递归版本
我们先创建一个类
public static class WaiteUse {
public int l;
public int r;
public WaiteUse(int left, int right) {
l = left;
r = right;
}
}
这个方便我们记录等待排列的左右是多少
public static void quickSort2(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
int N = arr.length;
swap(arr, (int) (Math.random() * N), N - 1);
//第一次处理,获得左右值。
int[] help = netherlandsFlag(arr, 0, N - 1);
int curLeft = help[0];
int curRight = help[1];
Stack<WaiteUse> stack = new Stack<>();
// 压入两个要等待排序的数组区间
stack.push(new WaiteUse(0, curLeft - 1));
stack.push(new WaiteUse(curRight + 1, N - 1));
// 跳出循环的条件是栈为空
while (!stack.isEmpty()) {
WaiteUse waiteUse = stack.pop();
// 如果碰到两个值相等的就跳过
if (waiteUse.l < waiteUse.r) {
swap(arr, waiteUse.l + (int) (Math.random() * (waiteUse.r - waiteUse.l + 1)), waiteUse.r);
help = netherlandsFlag(arr, waiteUse.l, waiteUse.r);
curLeft = help[0];
curRight = help[1];
// 压入新获得的两个没有排序的数组区间
stack.push(new WaiteUse(waiteUse.l, curLeft - 1));
stack.push(new WaiteUse(curRight + 1, waiteUse.r));
}
}
}
这边就是快速排序的所有内容了。