快速排序
基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分小,比另一部分的关键字大,则可分别对这两部分记录继续进行排序,以达整个序列有序。
1.时间复杂度:O(nlogn)。
2.空间复杂度:O(1)。
3.稳定性:不稳定。
图解:
注解:给一组数字,选取最左边数字作为主元,也就是将要放在中间的值,将这组数字分为左边都比主元小,右边都比主元大,如下图:
注解:此时分为左边比主元小,右边比主元大的情况,主元4的位置就确定在这儿了,下来将主元左边部分及右边部分继续按照刚才的办法去排序,即选取2为当前主元,对2,1,3这组数字进行排序,右边选取7作为当前主元,对7,6,8,5这组数字进行排序,划分好之后,最左右再进行同理划分,结果如下:
此时的结果已经是一组有序的数字。
单路快速排序
目标:arr [ l...p - 1] < arr [ p ] < [ p + 1... r ]
//:p是主元的下标,l是指当前数组最左边元素下标,r是指当前数组最右边元素下标。
操作:arr [ l + 1... j ] < v < arr [ j + 1 ... i ]
//:让左边元素都比主元小,右边元素都比主元大。
图解:
如图所示一组数字,确定最左边,也就是L对应的元素为主元,R指向最右边元素,j从L开始,i从j+1开始,要满足,从L+1到j都是小于V的,从j+1到i的值都是大于v的。
我们先开始判断i的对应的值,它比v小,那么就让i和j+1进行交换,此时,i和j+1对应的是同一个元素,所以不动,然后进行++操作,即i++,向后移,j++,说白了j就是主元左边第一个小于主元的元素,i就是最后一个大于主元的元素,如下图:
此时i对应的元素大于主元,直接把i向后移,然后7也大于主元,再次向后移,此时i移到了2所在的位置,对应的值小于主元,我们让i对应的值和j+1进行交换,交换之后j++,结果如下:
接下来再次进行相同操作,将i向后移,找到一个小于住院的元素,移到3对应的位置,将i对应值与j+1进行交换, 然后i++,j++,结果如下:
此时满足,从L+1到j都是比主元小的,从j+1都是比主元大的,最后将v与j进行交换。把4确定到中间,分成了左右两个部分,然后对两边再进行同理的排序,直到有序即可。下面我们进行代码操作一下:
package p4;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Random;
public class QuickSort {
//单路快速排序
public static void main(String[] args) {
//随机生成数组
int[] arr = new int[20];
Random random = new Random();
for(int i = 0; i < arr.length; i++){
arr[i] = random.nextInt(50 );
}
//中间用递归
quickSort(arr,0,arr.length - 1);
//打印看是否排好
System.out.println(Arrays.toString(arr));
}
private static void quickSort(int[] arr, int l, int r) {
//考虑是否只有一个元素,要是只有一个元素就不用往下继续了
if (l >= r){
return;
}
//分解,将原来的分为左右两部分,然后再将分好的左右两部分继续分。
int p = partition(arr,l,r);
quickSort(arr,l,p - 1);
quickSort(arr,p + 1,r);
}
private static int partition(int[] arr, int l, int r) {
int v = arr[l];
int j = l;
//i是主动的,j是被动的
for (int i = l + 1; i <= r; i++){
//判断i是否小于v,v就是主元对应的值,若小于,则让i和j+1的值进行交换
if (arr[i] < v){
swap(arr,j + 1,i);
j++;
}
}
swap(arr,l,j);
return j;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[i];
arr[j] = temp;
}
}
结果如图所示,由于产生的结果是随机的,所以我们在这里随便产生一个结果如下:
优化:
1.判断数组长度
private static void quickSort(int[] arr, int l, int r) {
//考虑是否只有一个元素,要是只有一个元素就不用往下继续了
// if (l >= r){
// return;
// }
if (r - 1 <= 10){
ShellSort.shellSort(arr,l,r);
return;
}
2.在partition处优化:
先让l值和后面的一个角标交换,叫叫换的当做目标值,然后进行交换。
private static int partition(int[] arr, int l, int r) {
//优化
swap(arr,l, (int) (Math.random() * (r - 1 + 1) + 1));
int v = arr[l];
int j = l;
//i是主动的,j是被动的
for (int i = l + 1; i <= r; i++){
//判断i是否小于v,v就是主元对应的值,若小于,则让i和j+1的值进行交换
if (arr[i] < v){
swap(arr,j + 1,i);
j++;
}
}
swap(arr,l,j);
return j;
}
到此我们的单路快速排序就完啦!
双路快速排序
目标:arr[l ... p - 1] <= arr[p] =< arr[p + 1... r]
操作:arr[l + 1 ... i] <= v <= arr[j ... r]
注解:i从前往后走,j从后往前走 ,在极端情况下,i可能会走到r。先判断i是否走到r以及i对应的元素值是否小于主元元素的值,都不满足则i++,即i向右移,直到找到一个不小于主元的元素,再看j,看j是否移动到l+1的位置以及判断j对应位置的值是否大于v,满足交换,不满足就j--,直到找到一个不大于主元的元素,即j向左移,此时判断i和j是否碰头,没碰头,则把i和j的值进行交换,如图所示:
此时交换了3和4,接下来i继续向后移,找到一个不小于主元的元素停,再移动j,找到一个不大于主元的元素停,如图所示:
接下来,i++,j--,让v对应的值和j对应的值交换,此时j对应的值就是确定好的中间值,此时这组数也被分为两部分,然后再对两部分的数进行排序,同理,直到有序即可。
代码实现部分:
package p4;
import java.util.Arrays;
import java.util.Random;
public class QuickSort02 {
public static void main(String[] args) {
//随机生成数组
int[] arr = new int[20];
Random random = new Random();
for(int i = 0; i < arr.length; i++){
arr[i] = random.nextInt(50 );
}
//中间用递归
quickSort(arr,0,arr.length - 1);
//打印看是否排好
System.out.println(Arrays.toString(arr));
}
private static void quickSort(int[] arr, int l, int r) {
if (l >= r){
return;
}
int p = partition(arr,l,r);
quickSort(arr,l,p - 1);
quickSort(arr,p + 1,r);
}
private static int partition(int[] arr, int l, int r) {
swap(arr,l, (int) (Math.random() * (r - 1 + 1) + 1));
int v = arr[l];
int i = l + 1;
int j = r;
while(true){
while(i <= r && arr[i] < v){
i++;
}
while (j >= l + 1 && arr[j] > v){
j--;
}
if (i > j){
break;
}
swap(arr,i,j);
i++;
j--;
}
return j;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[i];
arr[j] = temp;
}
}