快速排序
快速排序是使用分治的思想,先找出基准,以该基准进行二分分治递归
递归首先要找出递归出口,那何处为递归出口呢,questSort方法是递归体,当其left与right相遇则说明整个数组已遍历完成,可以退出循环
-
双向指针
指的是使用两个不同方向的指针
思路:(保证数组左边的数一直比基准小,数组右边的值一直比基准大)
步骤:1,从右边一直遍历,直到找到比基准小的值,停留在该下标(为什么需要从右边开始? 看代码注释)
2,从右边第2个数字开始遍历,一直到找到比基准大的下标
3,交互左右指针的下标所存的内容,然后继续上面的方法,一直到左右两个指针相遇
4,左右两个指针相遇时,将左边的指针的值与基准交换,返回左边的指针的下标,这个为基准值
常规做法是使用双向指针去完成,但是注意需要先从右开始遍历,再从做遍历,
public static void questSort(int[] a, int left , int right){ //递归结束条件 if(left>=right){ return ; } //找到基准 int patten = patten(a, left, right); //左边排序 questSort(a,left,patten-1); //有有右边排序 questSort(a,patten+1,right); } //分治,双向循环 // 找出基准的下标 public static int patten(int[] a, int start ,int end) { //双向指针 int left = start; int right = end; int temp = a[start]; //这里默认使用数组开头的第一个数做为基准 while (left != right) { //必须右边针先移,为什么???因为如果没有先控制右边的过来,会导致,交换的值 //left 跳到大的一部分,导致排序有误差 //为什么要小于或者等于,控制指针左移动 while (a[right] > temp && left < right) { right--; } //控制left指针,右移,a[left] 必须<=,不然出现有与temp一样的值会出现死循环,必须 索引值都 //扫描到 while (a[left] <= temp && left < right) { left++; } if (left < right) { int temp2 = a[right]; a[right] = a[left]; a[left] = temp2; } } //pivot 和指针重合点交换 // int pivote = a[left]; a[start] = a[left]; a[left] = temp; return left; }
-
单向指针
指的是使用两个相同方向的指针
思路:设置一个mark指针,mark一开始指向第一个元素(即基准元素) ,然后设置另一个指针(i)循环向右移动,当i遇到比基准小的值则停下来,则mark的指针向下移一位,并与i所在的位置交换值,保证mark指针左边以及其指向的值都是小于或是等于基准元素的。最后将mark的值与基准交换,返回基准值的位置mark
//单向循环,前后指针法 public static void questSort2(int[] a,int left ,int right){ //递归结束条件 if(left >= right){ return; } //找出基准元素 int provid = patten2(a, left, right); System.out.println(provid); //分治 questSort2(a,left,provid-1); questSort2(a,provid+1,right); } //单向,前后指针 public static int patten2(int[] a , int start , int end){ // System.out.println(Arrays.toString(a)); int left = start+1; int right = end; int temp = a[start];//第一个 //mark向左是小于temp的值 int mark = start; while(left<=right){ //找到temp小的值 if(a[left]<=temp){ mark++; //交换 int k = a[left]; a[left] = a[mark]; a[mark] = k; } left++; } a[start] = a[mark]; a[mark] = temp; return mark; }
-
挖坑法
挖坑法实际上与双向指针类似,只是先把第一个数暂时存起来,然后从右边开始找一个小于基准数去填左边那个小的数,接着右边的那个数就为空了(假设填补后,就为空),那现在就从做找一个大于基准的数来填这个空,但左右两个指针相遇,则结束
//快排,挖坑
public static void questSort5(int[] a, int left ,int right){
//递归结束
if(left>=right){
return ;
}
int patten5 = patten5(a, left, right);
questSort5(a,left,patten5-1);
questSort5(a,patten5+1,right);
}
//挖坑法
private static int patten5(int[] a, int left, int right) {
int i = left;
int j = right;
int temp = a[left];
while(i!=j){
while(a[j] >= temp && i<j){
//j 找小
j--;
}
a[i] = a[j];
while(a[i]< temp && i<j){
//找大
i++;
}
a[j] = a[i];
}
a[i] = temp;
return i;
}