前导课
划分成<=P区和>P区
![](https://img-blog.csdnimg.cn/img_convert/a68935031bed99ac10a3ac8a455ef586.png)
![](https://img-blog.csdnimg.cn/img_convert/3157ad54753808d5461e0e15f94b27d4.png)
以最后一个元素作为划分值---假设为P,将整个数组划分成[ 小于等于P的区域 大于P的区域]
每个区域里无序。
操作流程:
从左往右遍历,有两个分支逻辑
①若当前数<=P,则当前数和小于等于区的下一数交换,小于等于区右扩,指针移动到下一个位置(包括自己和自己交换)
②若当前数 > P,指针直接移动到下一个位置
指针来到终点位置时,停止循环
//将arr分成 [<=p区 >p区]
public static void splitNum1(int[] arr) {
int lessEqualR = -1;//用此变量来表示<=p区域的右边界,区域扩到哪就写哪
int index = 0;//遍历arr用
int N = arr.length;
while (index < N) {
if (arr[index] <= arr[N - 1]) { //用数组中最后一个元素做划分值。即p
//当前数<=p,则当前数和小于等于区的下一个位置做交换。交换完,index自增1.
//由于lessEqualR也采用的自增,所以也早已经完成了右扩的任务
//用lessEqualR的时候,是加1之后用1,用完后,还要加1更新,所以,直接写成++lessEqualR
//用index的时候,是直接用,用完后要加1更新,所以,写成index++
swap(arr, ++lessEqualR, index++);
//这若写成lessEqualR++,则是错的。
} else {
index++;//直接跳下一个
}
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
划分成<p区=p区>p区
将arr划分成[<p区 =p区 >p区]
操作流程:要用到两个区。还是拿最后一个数做划分值。还是从左向右遍历。
不同于上面,此流程中,刚开始的时候,小于区里没有一个数,但大于区里有最后一个数。
![](https://img-blog.csdnimg.cn/img_convert/08da93cf5d3bd5c9d4a50c1b4a4e60d6.png)
流程:
①若当前数<P,此数与小于区的下一个数交换,小于区右扩,index移到下一位
②若当前数 >P,,此数与大于区的前一个数交换,大于区左扩,index不动,因为此时index所指向的是刚从右边交换过来的数,还没看过,所以index停留在原地 ,从而看这个新数。
③若当前数=P,index直接移到下一位
当index与大于区的左边界撞上的时候,停止。然后单独让P与大于区的第一个数交换,至此,三个分区划分完毕
![](https://img-blog.csdnimg.cn/img_convert/250fac962e2d3411b6ec71cc7fff5a67.png)
![](https://img-blog.csdnimg.cn/img_convert/b54dc25e83ba1e5b6e143d7b232337c8.png)
//将[<p区 =p区 >p区]
public static void splitNum2(int[] arr) {
int N = arr.length;
int lessR = -1;//小于区的右边界,一开始在-1.
int moreL = N - 1;//大于区域的左边界,扩到哪就写到哪。一开始在N-1。即一开始大于区域就有一个数arr[N-1]
int index = 0;//遍历arr
// arr[N-1]的值做划分值
while (index < moreL) {//index没撞上大于区的左边界时继续。撞上时,则结束循环
//一共三个逻辑。很简单
if (arr[index] < arr[N - 1]) {//当前数小于划分值,放小于区,小于区增长,index继续下一个位置
swap(arr, ++lessR, index++);
} else if (arr[index] > arr[N - 1]) {//当前数大于划分值,放大于区,大于区增长,index不动。停在原地看刚从右边换过来的数,因为此数
//是刚换过来的,index这个指针还没看过他,所以,不动,留在原地看。
//和大区域的前一个数交换。swap里传的是下标。
swap(arr, --moreL, index);
} else {
index++;//上面都没中,说明当前数等于划分值,直接往下跳
}
}
//单独让划分值与大于区的第一个值交换
//划分值就是arr中的最后一个,也是大于区的最后一个,在上面的过程中,它一直未动,稳坐泰山。
//大于区的第一个值就是左边界上的值
swap(arr, moreL, N - 1);
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
快排
![](https://img-blog.csdnimg.cn/img_convert/aeee79fc21e708fb5ab7c6305025ffae.png)
递归下去,数组总有排好的时候。
在 前导课 里,将数组arr划分成3个区域的操作,称为 分区(动词),即partition。划分、分割、分区,怎么叫都行,你能理解它的神就ok了,名字只是形。
![](https://img-blog.csdnimg.cn/img_convert/222dd7be2ddcf2bee234a19b742ad31a.png)
这是在抽象。大范围和小范围的操作是一样的。这就是递归。
public static int[] partition(int[] arr, int L, int R)
返回的int[]是等于区域的左右边界,即等于区里的在左右边上的元素的下标。也就是说,
把arr[L....R]划分成三部分后,我返回等于区域的左右边界
,为的是,告知上面,我这个等于区是有序的,左边和右边是无序的,左右需要排序,
![](https://img-blog.csdnimg.cn/img_convert/6fde49bd2cc977b15ec92bdbb41aae3a.png)
详细注释版代码
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//partition有分治、分割、分区的意思
// arr[L...R]范围上,拿arr[R]做划分值,
// L....R < = >
public static int[] partition(int[] arr, int L, int R) {
//一定深刻记住,这是在[L...R]上操作
//准备三个变量:
int lessR = L - 1;//小于区的右边界
int moreL = R;//大于区的左边界
int index = L;//index从L遍历。
while (index < moreL) {//写成 !=也可。index没撞上大于区的左边界时,继续
if (arr[index] < arr[R]) {//arr[R]就是划分值
swap(arr, ++lessR, index++);
} else if (arr[index] > arr[R]) {
//当前值大于划分值,则与大于区的前一个数交换,大于区左扩,idnex不动!!!
swap(arr, --moreL, index);
} else {
index++;//当前值等于划分值,直接跳到下一个位置
}
}
//最后,单独处理划分值(即最后一个数,也就是R所在的位置。在上述过程中它一直没动,稳坐泰山),让他与大于区的第一个数(即moreL所在位置)
swap(arr, moreL, R);
return new int[] { lessR + 1, moreL };//返回等于区域的左右两个边界。2 3 4 4 4 8 5 ,返回等于区的第一个4的下标和最后一个4的下标。
// 用lessR和moreL来表示,就是lessR+1位置、moreL位置
//这个边界是为了告知左侧和右侧是无序的,需要排序
}
//快排的主函数
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
public static void process(int[] arr, int L, int R) {
//base case
//L>R是无效范围,直接return,L=R时只有一个数,无需排,直接return,所以,可以整合写到一起
/*
if(L>R) return;
if(L == R) return;*/
if (L >= R) {
return;
}
//上面不中。说明 L<R,不止一个数了。开始排
//arr[L...R]上拿arr[R]做划分值进行划分,划分成3个区域,划分出的等于区的左右边界给给我(边界包括等于区的值).也就是说,等于区是从哪到哪,equalRange里存的就是这个。
int[] equalRange = partition(arr, L, R);
//equalRange[0] 是等于区的第一个值的下标
//equalRange[1] 是等于区的最后一个值的下标
process(arr, L, equalRange[0] - 1);//左边无序,排去吧
process(arr, equalRange[1] + 1, R);//右边无序,排去吧
}
无注释版+含对数器
/**
* @author wbj
* @create 2023-03-11 15:17
*/
public class QuickSortReview {
public static void quickSort(int[] arr){
if(arr == null || arr.length < 2){
return;
}
process(arr,0,arr.length-1);//0到arr.length-1
}
//让arr[l...r]范围上有序
public static void process(int[] arr,int l,int r){
if(l >= r){
return;
}
int[] equalRange = partition(arr, l, r);
process(arr,l,equalRange[0]-1);
process(arr,equalRange[1]+1,r);
}
//将arr[l...r]分成3个区域
public static int[] partition(int[] arr,int l,int r){
int lessR = l-1;
int moreL = r;
int index = l;
while (index < moreL){
if(arr[index] < arr[r]){
swap(arr,++lessR,index++);
} else if (arr[index] > arr[r]) {
swap(arr,--moreL,index);
}else{
index++;
}
}
swap(arr,r,moreL);
return new int[]{lessR+1,moreL};
}
public static void swap(int[] arr,int i,int j){
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
//test
public static int[] getRandomArray(int size,int possible){
int[] arr = new int[(int) (Math.random() * size) + 1];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * possible);
}
return arr;
}
public static int[] copyArray(int[] a){
if(a == null || a.length == 0){
return null;
}
int[] arr = new int[a.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = a[i];
}
return arr;
}
public static void main(String[] args) {
int testTimes = 100;
int size = 400;
int possible = 1000000;
System.out.println("测试开始");
for (int i = 0; i < testTimes; i++) {
int[] array = getRandomArray(size, possible);
int[] copyArray = copyArray(array);
quickSort(array);
Arrays.sort(copyArray);
for (int j = 0; j < array.length; j++) {
if(array[j] != copyArray[j]){
System.out.println("error");
}
}
}
System.out.println("结束");
}
}
复习
自己默写一遍。能写出来,说明你真正掌握了
/**
* @author wbj
* @create 2023-03-23 15:33
*/
public class QuickSort {
public static void quickSort(int[] arr){
if(arr == null || arr.length <2){
return;
}
process(arr,0,arr.length-1);
}
public static void process(int[] arr,int l ,int r){
if(l >= r){
return;
}
int[] equalRange = partition(arr, l, r);
process(arr,l,equalRange[0]-1);
process(arr,equalRange[1]+1,r);
}
public static int[] partition(int[] arr,int l,int r){
int lessR = l-1;
int moreL = r;
int index = l;
while (index != moreL){
if(arr[index] < arr[r]){
swap(arr,++lessR,index++);
} else if (arr[index] > arr[r]) {
swap(arr,--moreL,index);
}else{
index++;
}
}
swap(arr,moreL,r);
return new int[]{lessR+1,moreL};
}
public static void swap(int[] arr,int i,int j){
int a = arr[i];
arr[i] = arr[j];
arr[j] = a;
}
public static void main(String[] args) {
int[] arr = {1,6,-2,-5,9,0,-88,77};
quickSort(arr);
for (int a : arr){
System.out.print(a+" ");
}
}
}