快速排序Quick Sort(其三)-三路快排
存在的问题:
在普通快速排序和二路快排中都没有考虑等于arr[l]的问题,只是把数组分成小于arr[l]和大于arr[l]的两部分,而三路快速排序则是把数组分成小于arr[l],等于arr[l],大于arr[l]的三部分,这样分隔之后在递归的过程中,对于等于arr[l]的部分我们就不用了管了,下次递归时不必递归等于arr[l]的部分了,只需要递归的对小于arr[l]和大于arr[l]的部分进行同样的快速排序就好了。
解决方法:
Partition具体过程:
设置索引lt指向小于arr[l]的最后一个元素的位置,arr[l+1,…,lt]<arr[l],同样的设置gt指向大于arr[l]的第一个元素的位置,arr[gt,…,r]>arr[l],
现在在处理的元素为i,则arr[lt+1,…,i-1]==arr[l]。
当前待处理的元素arr[i] ==arr[l]时,i++就可以。
当前待处理的元素arr[i] <arr[l]时,将arr[i]这个元素与等于arr[l]的第一个元素(arr[lt+1])进行交换,同时lt++,i++。
当前待处理的元素arr[i] >arr[l]时,将arr[i]与大于arr[l]的部分的前一个元素(arr[gt-1])进行交换位置,同时gt–,指向大于arr[l]的第一个元素。此时i这个元素不用维护,i指向的元素没有被处理,是从gt-1这个位置换过来的。
遍历结束后,lt指向小于arr[l]的最后一个元素的位置,gt指向大于arr[l]的第一个元素的位置,i与gt重合。
最后将arr[l]与arr[lt]进行交换就可以了,此时lt指向等于arr[l]的第一个元素的位置。
public void quickSort(E[] arr,int n){
quickSort3(arr,0,n-1);
}
/**
* 三路快速排序
* 将arr[l,...,r]分为<v,==v,>v三部分
* 之后递归对<v,>v两部分继续进行三路快速排序
* @param arr
* @param l
* @param r
*/
private void quickSort3(E[] arr, int l ,int r){
if(l>=r){
return ;
}
//三路快排不是简单地返回一个索引p,对(l- p-1)与(p+1,r)进行递归就好了
//三路快排中等于v的是一个区间
//partiton
int index = (Math.abs(new Random().nextInt())%(r-l+1))+l;
E t = arr[index];
arr[index] = arr[l];
arr[l]= t;
E v = arr[l];
int lt = l ;//arr[l+1,...,lt]<v
int gt = r+1;//arr[gt,...,r]>v
int i = l+1;//arr[lt+1,...,i)==v
while (i<gt) {
if(arr[i].compareTo(v)<0){
E temp = arr[i];
arr[i] = arr[lt+1];
arr[lt+1] = temp;
lt ++;
i++;
}else if(arr[i].compareTo(v)>0){
E temp = arr[gt-1];
arr[gt-1] = arr[i];
arr[i] = temp;
gt --;
}else{
//arr[i] == v
i++;
}
}
E temp = arr[l];
arr[l] = arr[lt];
arr[lt] = temp;
quickSort3(arr,l,lt-1);
quickSort3(arr,gt,r);
}
分治算法:分而治之,将原问题分割成同等结构的子问题,之后将子问题逐一解决后,原问题也就得到了解决。快速排序就是典型的分治算法,将原问题分割成同等结构的子问题,之后通过递归算法将子问题逐一解决。