快速排序法一般来说可以分为三种,分别是:
①基础快速排序法
②双路快速排序法
③三路快速排序法
下面分别对以上三种快排进行梳理。
基础快速排序法
排序思路:首先传入待排序数组,使用变量l表示数组下标起点,使用变量r表示数组下标终点,然后取数组第一个元素e做中介,使用循环逐步将数组元素分为比e小和不比e小的两部分;使用变量j记录左侧比e小的元素个数下标(初始值为l),循环内部使用变量i从l+1开始遍历到r,如果array[i]小于e,那么array[j+1]与array[i]进行互换然后j++,否则直接i++;循环结束后,变量j位于比e小的那部分的最尾部下标处,将数组待排序最左侧元素与array[j]交换,最后得到l~j-1为比e小的部分,j+1~r为不比e小的部分。接着对l~j-1和j+1~r这两部分继续上述的排序操作,直到l>=r,排序结束。
代码实现:
//基础版快速排序法
public static void quickSortBase(int[] array){
__quickSortBase(array,0,array.length-1);
}
public static void __quickSortBase(int[] array,int l,int r){
int temp;
if(l>=r){
return ;
}
int e = array[l];
int j = l;//保存<e部分尾部的下标
for(int i=l+1;i<=r;i++){//将数组分为<e和>=e两部分
if(array[i]<e){
temp = array[j+1];
array[j+1] = array[i];
array[i] = temp;
j++;
}
}
//排序完成后,将array[j]与array[l]进行交换,此时l~j-1为<e部分,j+1~r为>=e部分
temp = array[j];
array[j] = array[l];
array[l] = temp;
//继续进行排序
__quickSortBase(array,l,j-1);
__quickSortBase(array,j+1,r);
}
生成20个0~66的数,进行测试:
此版本可以进行的优化:
①每次排序前,先将数组最左边的数和数组中的元素随机互换一下,然后再取数组最左边的元素e做中介。
作用:解决对几乎整体有序的数组进行排序时,会导致效率低下的问题。
public static void __quickSortBase(int[] array,int l,int r){
int temp;
if(l>=r){
return ;
}
//优化1:先随机取下标,然后与最左元素交换下
int index = (int) (Math.random()*(r-l)+l);
temp = array[l];
array[l] = array[index];
array[index] = temp;
int e = array[l];
int j = l;//保存<e部分尾部的下标
for(int i=l+1;i<=r;i++){//将数组分为<e和>=e两部分
if(array[i]<e){
temp = array[j+1];
array[j+1] = array[i];
array[i] = temp;
j++;
}
}
//排序完成后,将array[j]与array[l]进行交换,此时l~j-1为<e部分,j+1~r为>=e部分
temp = array[j];
array[j] = array[l];
array[l] = temp;
//继续进行排序
__quickSortBase(array,l,j-1);
__quickSortBase(array,j+1,r);
}
②当待排序部分元素个数小的时候,进行优化版的插入排序,提高效率
public static void __quickSortBase(int[] array,int l,int r){
int temp;
// if(l>=r){
// return ;
// }
//优化2:使用优化版的插入排序改进效率
if(r-l<=15){
insertionSortBetter(array, l, r);
return ;
}
//优化1:先随机取下标,然后与最左元素交换下
int index = (int) (Math.random()*(r-l)+l);
temp = array[l];
array[l] = array[index];
array[index] = temp;
int e = array[l];
int j = l;//保存<e部分尾部的下标
for(int i=l+1;i<=r;i++){//将数组分为<e和>=e两部分
if(array[i]<e){
temp = array[j+1];
array[j+1] = array[i];
array[i] = temp;
j++;
}
}
//排序完成后,将array[j]与array[l]进行交换,此时l~j-1为<e部分,j+1~r为>=e部分
temp = array[j];
array[j] = array[l];
array[l] = temp;
//继续进行排序
__quickSortBase(array,l,j-1);
__quickSortBase(array,j+1,r);
}
双路快速排序法
排序思路:首先传入待排序数组,使用变量l表示数组下标起点,使用变量r表示数组下标终点,然后取数组左侧第一个元素v做中介,声明变量i初始值为l+1,声明变量j初始值为r,如果i<=r&&array[i]比v小,i++,直到array[i]>=v;如果j>=l+1&&array[j]比v大,j--,直到array[j]<=v;交换array[i]和array[j],确保下标从l~i的数组元素均小于等于v,下标从j~r的数组元素均大于等于v,接着i++,j--;循环完成后,j的位置是数组中<=v的最后一个元素位置,将其与array[l]互换,接着继续对l~j-1和j+1~r的两部分进行双路快排。
代码实现:
//双路快排,解决大量重复值的数组排序
public static void quickSort2(int[] array){
_quickSort2(array,0,array.length-1);
}
public static void _quickSort2(int[] array,int l,int r){
//数组数量小时,用优化版插入排序提高效率
if(r-l<=15){
insertionSortBetter(array, l, r);
return ;
}
//优化:先随机取下标,然后与最左元素交换下
int index = (int) (Math.random()*(r-l)+l);
int temp = array[l];
array[l] = array[index];
array[index] = temp;
int v = array[l];//最左侧元素做中介
int i = l+1,j = r;
while(true){
while(i<=r&&array[i]<v){i++;}//如果array[i]小于v,i++;直到碰到不小于v的元素时停止
while(j>=l+1&&array[j]>v){j--;}//如果array[j]大于v,j--;直到碰到不大于v的元素时停止
if(i>j){break;}
//交换array[i]和array[j],保证下标从l~i的元素值均小于等于v,下标从j~r的元素值均大于等于v
temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
//循环结束后,j位于<=v的最后一个元素位置,交换array[j]和array[l];此时l~j均小于等于v,下标从j~r的元素值均大于等于v,array[j]==v
temp = array[l];
array[l] = array[j];
array[j] = temp;
_quickSort2(array,l,j-1);
_quickSort2(array,j+1,r);
}
三路快速排序法
排序思路:首先传入待排序数组,使用变量l表示数组下标起点,使用变量r表示数组下标终点,然后取数组左侧第一个元素v做中介,声明变量i初始值为l+1,声明变量lt初始值为l,声明变量gt初始值为r+1,先将数组分为三部分,array[l+1]~array[lt]的部分<v,array[gt]到array[r]的部分>v,array[lt+1]~array[i]的部分==v;当i<gt时数组进入循环处理。如果array[i]<v,将其与lt+1交换,并且lt++,此时l+1~lt的部分小于v,lt到i的部分等于v;如果array[i]>v,将其与gt-1交换,并且gt--,此时gt~r的部分大于v;如果array[i]==v,直接i++即可。循环终止时,lt位于array中<v部分的最后一位,将其与array[l]进行互换,此时array[l]~array[lt-1]的部分<v,array[lt]到array[gt-1]的部分==v,array[gt]到array[r]的部分>v。接着继续对小于v和大于v的两部分进行三路快排。
代码实现:
// 三路快排,将数组分为<v,==v,>v三部分处理
public static void quickSort3(int[] array) {
_quickSort3(array, 0, array.length - 1);
}
public static void _quickSort3(int[] array, int l, int r) {
// 数组数量小时,用优化版插入排序提高效率
if (r - l <= 15) {
insertionSortBetter(array, l, r);
return;
}
// 优化:先随机取下标,然后与最左元素交换下
int index = (int) (Math.random() * (r - l) + l);
int temp = array[l];
array[l] = array[index];
array[index] = temp;
int v = array[l];// 最左侧元素做中介
int i=l+1,lt=l,gt=r+1;//array[l+1...lt]<v,array[gt...r]>v,array[lt+1....i]==v
while(i<gt){
if(array[i]<v){//将其与lt+1交换,并且lt++,此时l+1~lt的部分小于v,lt到i的部分等于v
temp = array[i];
array[i] = array[lt+1];
array[lt+1] = temp;
lt++;
i++;
}
else if(array[i]>v){//将其与gt-1交换,并且gt--,此时gt~r的部分大于v
temp = array[i];
array[i] = array[gt-1];
array[gt-1] = temp;
gt--;
}else{//直接i++
i++;
}
}
//循环结束后,lt位于小于v的部分的最后一个元素,将其与最左侧交换
temp = array[l];
array[l] = array[lt];
array[lt] = temp;
//继续对小于v和大于v的两部分进行三路快排
_quickSort3(array, 0, lt-1);
_quickSort3(array, gt, r);
}