private static void quickSortInternal(int[] array,int l,int r){
if (l >= r){
return;
}
//q就是当前的分区点pivot,partion()方法就是找到分区点并且返回这点的下标,下标为q
int q = partition(array,l,r);
//下面进行递归分区,下标左右两边的数组继续寻找pivot分区点,继续划分;
//递归就是继续调用本方法,传入新的数组以及分区;
quickSortInternal(array,l,q-1);
quickSortInternal(array,q+1,r);
}
2、下面是partioon()方法,分区函数,此方法作用就是随机选取一个元素作为分区点pivot,然后进行分区,就是将小的元素往前放,大的元素往后放;这里要注意一点,分区方法的操作,全部是在本数组中进行的,原地完成分区操作;
先来看看代码:
/**
* 分区点
* @param array 传入的数组
* @param l 左边界
* @param y 右边界
*/
private static int partition(int[] array,int l,int r){
//每次以第一个元素为分区点进行快排,
//对一个近乎有序并且特别大的数列进行快排会发生栈溢出
//因为栈内存不够用,递归不下去了;
int randomIndex = (int) ((Math.random()*(r-l+1))+l);//获得一个随机的下标,随机数不超过数组的长度-1
swap(array,l,randomIndex);//交换第一个元素跟随机的这个元素,这个swap方法自己写的
int v = array[l];
int j = l;
int i = l + 1 ;
for (;i <= r;i++){
if (array[i] < v){
swap(array,j+1,i);
j++;
}
}
swap(array,l,j);
return j;
}
画个图来解释一下代码,代码中的三个指针下标:L,i,j,(R不必解释,就是一个临界点)
如图所示:
如果数字集合中的数字重复度非常高,包含大量重复的元素,那么也会出现这种情况,也会导致数组长度不均衡,相同的元素都会放到分区点右边,分层下来后右边的元素会越来越多,此时分层下来的结果近乎n层,快排退化为O(n^2);
就是把相等的两个元素均衡的分配到基准值两边;这样的话分层下来后左右两个数组中相等的元素均衡分布;
跟前面的简单快排不同,这次我们增加了一个遍历数组的指针,两个指针,就是两个下标,一个是 i 从前往后遍历,另一个是 j 从后往前遍历,当i遇到 >pivot 的时候停下,等 j ,然后j遇到 < pivot 的时候停下并且与停下来的 i 所指向的元素交换;然后再次开始遍历;结束条件 j < i;
private static int partition2(int[] data,int left,int right){
//左下标跟右下标刚开始都分别指向小于pivot区域
//的前一个位置和大于pivot区域的后一个位置;
//当遍历开始后,交换开始后,i一直指向小于pivot区域的最后一个元素,
//j一直指向大于pivot的第一个元素;
int i = left+1;
int j = right;
int value = data[left];
while(i <= j){
while(data[i] < value){
i++;
}
while(data[j] > value){
j--;
}
if (i > j){
break;
}
swap(data,i,j);
i++;
j--;
}
swap(data,left,j);
return j;
}
/**
* 分区点
* @param array 传入的数组
* @param l 左边界
* @param y 右边界
*/
private static int partition3(int[] array,int l,int r){
//三个指针,lt永远指向小于pivot区域的最后个元素
//gt永远指向大于pivot区域的的第一个元素
//i一直向后遍历元素;跟双路快排优点差别,双路快排是两个指针同时
//分别从前向后、从后向前遍历;三路快排是三个指针,两个固定指向,一个从前向后遍历;
int value = array[l];
int lt = l;//刚开始lt指向小于v区域的前一个位置;即初始位置;
int i = l+1;
int gt = r+1;//同样gt后向前遍历,初始指向大于v区域的前一个位置;
//下面开始遍历,从l+1处开始遍历
while (i < gt){
if (array[i] < value){
swap(array,i,lt+1);
lt++;
i++;
}else if(array[i] > value){
swap(array,i,gt-1);
gt--;
//此处不能i++;因为当前i的元素是换回来的gt-1位置的元素
//必须再次比较
//前一个if入口的i++是因为当前i所指的元素就是换过来的小于
//v的值,所以直接遍历下一个元素就行;
// i++;
}else{
i++;
}
}
//注意:这里的循环完成后,一定是gt指向小于v的最后一个区域;
//所以这里可以直接交换l和gt的元素,然后返回gt 这个下标;
swap(array,l,lt);
return lt;
}