Algs4-2.3.18三取样切分
2.3.18三取样切分。为快速排序实现正文所述的三取样切分(参见2.3.3.2节)。运行双倍测试来确认这项改动的效果。
public class E2d3d18
{
public static void sort(Comparable[] a)
{
StdRandom.shuffle(a);
sort(a,0,a.length-1);
}
private static void sort(Comparable[] a,int lo,int hi)
{
if (hi<=lo) return;
int j=partition(a,lo,hi);
sort(a,lo,j-1);
sort(a,j+1,hi);
}
private static int partition(Comparable[] a,int lo,int hi)
{
int i=lo,j=hi+1;
int newVIndex=ThreeMedianIndex(a,lo,hi);
exch(a,lo,newVIndex);
Comparable v=a[lo];
while(true)
{
while(less(a[++i],v)) if(i==hi) break;
while(less(v,a[--j])) if(j==lo) break;
if(i>=j) break;
exch(a,i,j);
}
exch(a,lo,j);
return j;
}
//返回三取样切分元素索引
private static int ThreeMedianIndex(Comparable[] a,int lo,int hi)
{
//子数组少于3个元素时,第一个元素作为切分元素
if((hi-lo+1)<3) return lo;
//子数组有3个或以上元素时,取子数组前三个元素的中位数作为切分元素
////将原数组的前三个元素的索引作为新数组的值
Integer[] b={lo,lo+1,lo+2};
////使用插入排序法排序新数组b,按原数组的值进行排序。排序后的结果是原数组中小中大值对应的索引
for(int i=0;i<2;i++)
for(int j=i+1;j>0;j--)
if (less(a[b[j]],a[b[j-1]])) exch(b,j,j-1);
return b[1];
}
private static boolean less(Comparable v,Comparable w)
{ return v.compareTo(w)<0;}
private static void exch(Comparable[] a,int i,int j)
{
Comparable t=a[i];
a[i]=a[j];
a[j]=t;
}
private static void show(Comparable[] a)
{
for (int i=0;i<a.length;i++)
StdOut.print(a[i]+" ");
StdOut.println();
}
public static boolean isSorted(Comparable[] a)
{
for (int i=1;i<a.length;i++)
if(less(a[i],a[i-1])) return false;
return true;
}
public static void main(String[] args)
{
int N=Integer.parseInt(args[0]);
Double[] a=new Double[N];
for(int k=0;k<N;k++)
a[k]=StdRandom.random();
sort(a);
assert isSorted(a);
show(a);
}
}
--双倍测试比较
public class DoublingTest
{
public static double timeTrial(int N)
{
int MAX=1000000;
Integer[] a=new Integer[N];
for(int i=0;i<N;i++)
a[i]=StdRandom.uniform(-MAX,MAX);
Stopwatch timer=new Stopwatch();
//E2d3d18.sort(a);
Quick.sort(a);
return timer.elapsedTime();
}
public static void main(String[] args)
{
for(int N=250;true;N=N+N)
{
double time=timeTrial(N);
StdOut.printf("%7d %5.1f\n",N,time);
}
}
}
--三取样切分的另一种对中位数取值的方法略慢于快速排序取取中位数
//返回三取样切分元素索引
private static int ThreeMedianIndex(Comparable[] a,int lo,int hi)
{
//子数组少于3个元素时,第一个元素作为切分元素
if((hi-lo+1)<3) return lo;
//子数组有3个或以上元素时,取子数组前三个元素的中位数作为切分元素
////将原数组的前三个元素的索引作为新数组的值
Integer[] b={lo,lo+1,lo+2};
////按原数组的值对新数组进行排序。排序后的结果是原数组小中大值对应的索引
if(less(a[b[1]],a[b[0]])) exch(b,0,1);
if(less(a[b[2]],a[b[1]])) exch(b,1,2);
if(less(a[b[1]],a[b[0]])) exch(b,0,1);
return b[1];
}