前言
每件事的最后都会是好事 如果不是好事 说明还没到最后
经典快排
经典快排的思想其实就是:选择一个基准数,将大于这个基准数的数放在基准数的右边,小于等于的放在左边。然后再分别在这两个区域中选择基准数,再将这两个区域分别分为大于和小于等于两个区域,直到最后排好序。
- 首先我们将数组中的第一个数设为temp,也就是我们的基准数。将数组最后一个数设为j。
- 从j开始,一个个依次向前查找,找到第一个小于temp的数,停止。如图,第一个数3就小于基准数4,所以此时停下。
- 接着从第一个数依次向后查找,即图中的i。当找到第一个大于temp的数后,停下。
- 将i和j的数进行交换。
- 继续从j开始查找下一个小于temp的数。重复上面的 2 3 4。
- 当i和j相遇后,结束这次遍历。将i和temp进行交换后结束。
这样,我们一次操作就完成了。接下来,在进行递归,依次对两个区域进行上面的操作。最后整个数组将有序。
Code
public void quicksort (int[] s,int left,int right){
if (left>right){
return;
}
int i = left;
int j = right;
int temp = s[left];
while (i!=j){
while (i<j&&s[j] >= temp) j--;
while (i<j&&s[i] <= temp ) i++;
if (i<j){
int t = s[j];
s[j] = s[i];
s[i] = t;
}
}
s[left] = s[i];
s[i] = temp;
System.out.println();
quicksort(s,left,i-1);
quicksort(s,i+1,right);
}
改进后的快排
荷兰国旗问题
首先看一下经典的荷兰国旗问题。
现有红白蓝三个不同颜色的小球,乱序排列在一起,请重新排列这些小球,使得红白蓝三色的同颜色的球在一起。这个问题之所以叫荷兰国旗,是因为我们可以将红白蓝三色小球想象成条状物,有序排列后正好组成荷兰国旗。
将红白蓝三种颜色的球分别用0,1,2代替,我们就可以将这个问题视为一个数组排序问题。
解法一
遍历整个数组,记录下0,1,2的个数,再依次输出。
解法二
首先我们看另外一个题目
给定一个数组arr,和一个数num,请把小于num的数放在数组的 左边,等于num的数放在数组的中间,大于num的数放在数组的右边。
聪明的宝宝一定发现了,其实我们的荷兰国旗问题就是其中的Num=0的时候。那么,我们要怎么去做呢?
- 让cur指向数组的最左边,然后将less指向数组的最左边-1,more为最右边+1。(最左边-less的区域为小于区,more-最右边为大于区,所以一开始这两个区域都为0)
- 将cur和num进行比较,当大于num的时候就将more-1并与cur指向的数交换。小于num的时候就将less+1并与cur交换,交换后将cur+1。如果cur=num则直接Num++。
- 直到cur和more相遇则完成。假设图中的Num为3,执行情况将会如图所示。
这里我们要注意的一个问题就是,当大于num的时候,我们的cur是不会+1的,因为此时我们无法确保交换过来的数是大于还是小于或者是等于Num,所以这个数还需要进行一次比较。但是如果与less+1的数交换,交换过来的数是之前比较过的,所以无需再比较,此时cur就需要+1。
Code:
public static int[] partition(int[] arr, int l, int r, int num) {
int less = l - 1;
int more = r + 1;
while (l < more) {
if (arr[l] < num) {
swap(arr, ++less, l++);
} else if (arr[l] > num) {
swap(arr, --more, l);
} else {
l++;
}
}
return new int[] { less + 1, more - 1 };
//这里返回的是一个长度为2的数组,a[0]代表的是等于区域的第一个数,a[1]代表的是等于区域的最后一个数
改进快排
回顾一下普通快排,每一次它只解决了我们基准数一个数。假如我们用荷兰国旗问题来改进我们的过程,是不是就可以每一次解决多个相等的数呢?
Code:
public static void quickSort(int [] a ){
if (a==null||a.length < 2){
return;
}
quickSort(a,0,a.length-1);
}
private static void quickSort(int[] a, int left, int right) {
while(left<right){
int[] p = partiction(a ,left,right);
quickSort(a,left,p[0]-1);
quickSort(a,p[1]+1,right);
}
}
private static int[] partiction(int[] a, int left, int right) {
//和最右边的数进行比较
int less = left -1;
int more = right;
int cur = left;
while(cur<more){
if (a[cur]<a[right]){
swap(a,++less,cur++);
}else if (a[cur]>a[right]){
swap(a,--more,cur);
}else {
cur++;
}
}
swap(a,more,right);
return new int[] {less+1 , more };
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
随机快排
因为我们无法确保每一次划分的时候,正好划分在中间位置。所以我们采用随机快排,将数据状况变成一个概率问题。(通过长期期望证明复杂度为0(nlogn))
public static void quickSort(int[] arr, int l, int r) {
if (l < r) {
swap(arr, l + (int) (Math.random() * (r - l + 1)), r);//就是只要加这一行就好啦o(* ̄▽ ̄*)ブ
int[] p = partition(arr, l, r);
quickSort(arr, l, p[0] - 1);
quickSort(arr, p[1] + 1, r);
}
}