三向切分的快速排序
初始三向切分快速排序
在 标准快速排序 中,对于有大量重复元素的数组会多次使用重复元素进行切分和交换,针对有大量重复元素的数组,提出了三向切分,使用指针 lt 和 ht,v[lo+1,lt)小于v[lo],v(ht,hi]大于v[lo],v[lt,ht]等于v[lo],这样的话一次将所有等于v[lo]的元素全部排定。
import edu.princeton.cs.algs4.StdRandom;
/**
* 三向切分的快速排序
* @author XY
*
*/
@SuppressWarnings("rawtypes")
public class QuickSortC {
public static void sort(Comparable[] v){
StdRandom.shuffle(v);
int N=v.length;
sort(v, 0, N-1);
}
private static void sort(Comparable[] v,int lo,int hi){
if(hi-lo<5){
InsertSort.sort(v, lo, hi); return; }
int mid=mid(v, lo, lo+(hi-lo)/2, hi);
exchange(v, lo, mid);
int[] j=position(v, lo, hi);
sort(v, lo, j[0]);
sort(v, j[1], hi);
}
public static int mid(Comparable[] v,int i,int j,int k){
int a=v[j].compareTo(v[i]);
int b=v[j].compareTo(v[k]);
int c=v[i].compareTo(v[k]);
if(a*b<0) return j;
if(b*c>0) return i;
return k;
}
private static int[] position(Comparable[] v,int lo,int hi){
//指针lt左边的数组都小于v[lo],指针ht右边的数组都大于v[lo],lt和ht之间的等于v[lo]
int i= lo+1;
int lt =lo;
int ht=hi;
int a;
Comparable low=v[lo];
while(i<ht){
a=v[i].compareTo(low);
if(a<0) {exchange(v, i++,lt++);}
else if(a==0) i++;
else {exchange(v, i, ht--);}
}
v[lo]=v[lt];
v[lt]=low;
return new int[]{lt,ht};
}
public static boolean less(Comparable a, Comparable b) {
return a.compareTo(b) < 0;
}
public static void exchange(Comparable[] v, int a, int b) {
Comparable w = v[a];
v[a] = v[b];
v[b] = w;
}
public static boolean issorted(Comparable[] v) {
for (int i = 0; i < v.length - 1; i++) {
if (less(v[i + 1], v[i]))
return false;
}
return true;
}
public static void show(Comparable[] v){
StringBuffer sb=new StringBuffer();
for (int i = 0; i < v.length; i++) {
sb.append(" "+v[i]);
}
System.out.println(sb.toString());
}
public static void main(String[] args){
Double[] n={1.2,3.5,8.2,0.5,4.5,1.8,4.5,2.9,3.0,4.5,0.3,0.1,5.3,2.9,4.2,8.0,7.6};
sort(n);
show(n);
System.out.println(issorted(n));
}
}
改进的三向切分快速排序
前面的三向切分排序是将小于和大于切分元素的元素向左和向右移动,所以移动的是非重复元素,但是如果元素的重复个数远小于数组长度的时候,为了排定重复元素而移动大量的非重复元素会得不偿失,也就是三向切分一次排定重复元素减少的时间小于移动非重复元素带来的时间,这样排序时间并没有减少,针对这类情况提出了改进的三向切分:将等于v[lo]的元素往左右移动,使得[lo+1,p]等于v[lo],[q,hi]等于v[lo],(p,i]小于v[lo],[j,q)大于v[lo],完成之后将[lo+1,p]和[q,hi]往中间移动,形成和前面初始三向切分一样的状态。
import edu.princeton.cs.algs4.StdRandom;
/**
* 采用Bently三向切分和三取样切分排序、小段插入排序的快速排序
* @author XY
*
*/
@SuppressWarnings("rawtypes")
public class QuickSortD {
private static final int insert_level=8;
private static final int meadin_cutoff=40;
public static void sort(Comparable[] v){
StdRandom.shuffle(v);
sort(v,0,v.length-1);
}
public static void sort(Comparable[] v,int lo,int hi){
// 插入排序
if(hi-lo+1<insert_level){
InsertSort.sort(v, lo, hi);
return;
}
// 取中位数
if(hi-lo+1<meadin_cutoff){
int mid=mid(v, lo, (hi+lo)/2, hi);
exchange(v, lo, mid);
}else {
int eps=(hi-lo)/8;
int cut=(hi+lo)/2;
int mid=mid(v, mid(v, lo, lo+eps, lo+eps+eps),
mid(v, cut-eps, cut, cut+eps),
mid(v, hi, hi-eps, hi-eps-eps));
exchange(v, lo, mid);
}
// Bently&Mellroy 三向切分排序
int i=lo;int j=hi+1;int p=lo;int q=hi+1;
Comparable w=v[lo];
while(true){
while(i<hi && less(v[++i], w)){}
while(j>lo && less(w, v[--j])){}
if(i==j && v[i].compareTo(w)==0){
exchange(v, i, ++p);
break;
}
if(i>=j) break;
exchange(v, j, i);
if(v[i].compareTo(w)==0) exchange(v, i, ++p);
if(v[j].compareTo(w)==0) exchange(v, j, --q);
}
i=j+1;//切分处注意i和j的大小关系
for (int k = lo; k <= p; k++) {
exchange(v, k, j--);
}
for (int k = hi; k >= q; k--) {
exchange(v, k, i++);
}
sort(v,lo,j);
sort(v, i, hi);
}
public static int mid(Comparable[] v,int a,int b,int c){
return less(v[a],v[b])?
(less(v[b], v[c])? b:(less(v[a],v[c])? c:a)):
(less(v[c], v[b])? b:(less(v[a],v[c])? a:c));
}
// public static int[] position(Comparable[] v,int lo,int hi){
// int p=lo+1;int q=hi; int i=lo+1;int j=hi;
// Comparable w=v[lo];
// while(j-i>0){
// int a=v[i].compareTo(w);
// if(a<0) i++;
// else if (a>0) {
// exchange(v, i, j);
// }else{exchange(v, i++, p++);}
//
// int b=v[j].compareTo(w);
// if(b>0) j--;
// else if (b<0) {
// exchange(v, i, j);
// }else{exchange(v, j--, q--);}
// }
// if(v[p].compareTo(w)==0) p++;
// if(v[q].compareTo(w)==0) q--;
//
// return new int[]{p,q};
// }
public static int[] position(Comparable[] v,int lo,int hi){
int i=lo;int j=hi+1;int p=lo;int q=hi+1;
Comparable w=v[lo];
while(true){
while(i<hi && less(v[++i], w)){}
while(j>lo && less(w, v[--j])){}
if(i==j && v[i].compareTo(w)==0){
exchange(v, i, ++p);
break;
}
if(i>=j) break;
exchange(v, j, i);
// System.out.println(i+" "+j);
if(v[i].compareTo(w)==0) exchange(v, i, ++p);
if(v[j].compareTo(w)==0) exchange(v, j, --q);
}
i=j+1;
for (int k = lo; k <= p; k++) {
exchange(v, k, j--);
}
for (int k = hi; k >= q; k--) {
exchange(v, k, i++);
}
return new int[]{j,i};
}
public static boolean less(Comparable a, Comparable b) {
return a.compareTo(b) < 0;
}
public static void exchange(Comparable[] v, int a, int b) {
Comparable w = v[a];
v[a] = v[b];
v[b] = w;
}
public static void show(Comparable[] v){
StringBuffer sb=new StringBuffer();
for (int i = 0; i < v.length; i++) {
sb.append(" "+v[i]);
}
System.out.println(sb.toString());
}
public static boolean issorted(Comparable[] v) {
for (int i = 0; i < v.length - 1; i++) {
if (less(v[i + 1], v[i]))
return false;
}
return true;
}
public static void main(String[] args) {
Integer[] a={2,2,1,12,21,41,36,4,6,2,2,5,6,87,96,30,24,2,3,8,1,3,9,8,14,8,25,7,45,3,14,25,89,33,66,13,5};
sort(a);
show(a);
System.out.println(issorted(a));
}
}