2.3.23Java的排序库函数。在练习2.3.22的代码中使用Tukey‘s ninther方法来找出切分元素--选择三组,每组三个元素,分别取三组元素的中位数,然后取三个中位数的中位数作为切分元素,且在排序小数组时切换到插入排序。
public class E2d3d23
{
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)
{
//数组少于M个元素时使用插入排序
int M=8;
if (hi-lo+1<M)
{
InsertSort(a,lo,hi);
return;
}
//p的初值为lo+1,满足lo~p-1的元素=v
//i的初值为lo+1,p~i-1为0长,满足p~i-1的元素<v
//q的初值为hi,q+1~hi为0长,满足q+1~hi的元素=v
//j的初值为hi,j+1~q为0长,满足q+1~hi的元素>v
int p=lo+1,i=lo+1,q=hi,j=hi;
// StdOut.printf("lo=%d,i=%d,j=%d,hi=%d\n",lo,i,j,hi);
int newVIndex=TukeysNintherIndex(a,lo,hi,M);
exch(a,lo,newVIndex);
Comparable v=a[lo];
while(i<=j)
{
//当i<j时一定需要i位置元素与v对比,当出现数组只有两个元素v,<v时,i=j,此时如果不进行对比排序后的结果就无序的,所以i=j时也需要对比。
//由于i=j时还需要对比,那么可能会出现i越过j形成i>=j的情况。
while(i<=j)
{
int cmp=a[i].compareTo(v);
//StdOut.printf("ToRight i=%d,j=%d,cmp=%d,a[i]=%f,v=%f\n",i,j,cmp,a[i],v);
//当i位置元素<v时,i向右移动一个位置,此时p~i-1的元素<v
if (cmp<0) i++;
//当i位置元素=v时,交换i,p位置的元素,i,p指针向右移动一个位置,此时lo~p-1的元素=v,p~i-1的元素<v
else if (cmp==0) exch(a,i++,p++);
//当位置i的元素>v时,i指针暂停右移
else if(cmp>0) break;
}
//当i<j时一定需要j位置元素与v对比,
//当出现数组只有两个元素v,>v时,i=j,由于在上一个while中i位置元素已与v进行过对比,如果j位置元素再与v进行一次对比就多比较一次了,所以j位置元素与v的比较必要性不强。
//所以i=j时可以不进行对比了,那么意味着j向左移动时不可能会越过i位置形成i>j的情况,最多只可能是形成i=j的情况。
while(i<j)
{
int cmp=a[j].compareTo(v);
// StdOut.printf("ToRight i=%d,j=%d,cmp=%d,a[i]=%f,v=%f\n",i,j,cmp,a[i],v);
//当j位置元素<v时,j指针暂停左移
if (cmp<0) break;
//当j位置元素=v时,交换j,q位置的元素,j,q指针向左移动一个位置,此时q+1~hi的元素=v,j+1~q的元素>v
else if(cmp==0) exch(a,j--,q--);
//当j位置元素>v时,j向左移动一个位置,此时j+1~q的元素>v
else if(cmp>0)j-- ;
}
//i,j指针相遇或i越过j时形成i>=j的几种具体排列
//1)v,<v 此情况时i>j,i-1位置(含i-1)左边的元素<=v,右边的元素>=v。
//2)v,v,此情况时i>j,i-1位置(含i-1)左边的元素<=v,右边的元素>=v。
//3)v,>v,此情况时i=j,i-1位置(含i-1)左边的元素<=v,右边的元素>=v。
//4)v,>v,<v此情况时i<j需要交换i,j位置元素,并将i,j向前移动一位,此时i>j,i-1位置(含i-1)左边的元素<=v,右边的元素>=v。
//5)v,<v,>v此情况时i=j,i-1位置(含i-1)左边的元素<=v,右边的元素>=v。
//当i,j 指针相遇或越过时,结束本轮比较
if (i>=j) break;
//StdOut.printf("Exch i=%d,j=%d\n",i,j);
//上述第4点。
exch(a,i,j);
i++;
j--;
}
//依据上述5点的结论,得出位置i和i右边的元素>=v,保存i到j
j=i;
//左端=v元素与<v的元素段的右边交换。具体
//从左端向右将所有=v的元素与i-1位置到左边的元素交换,
//lo~i-1段,p无论是靠左或靠右或均分此段时,这种交换都将得到<v,=v的排列。
i--;
for (int k = lo; k < p; k++) exch(a, k, i--);
//右端=v端元素与>v的元素段的左端进行交换。
//从右端向左将所有=v的元素与j位置到右边的元素交换,
//j~hi段,q无论是靠左或靠右或均分此段时,这种交负都将得到=v,>v的排列。
for (int k = hi; k > q; k--) exch(a, k, j++);
// StdOut.printf("Move lo=%d,i-1=%d,j+1=%d,hi=%d\n",lo,i-1,j+1,hi);
// StdOut.println("Left Sort");
//对<v的左子数组再排序,此时i处在最右边的<v的位置上。
sort(a, lo, i);
//StdOut.println("Right Sort");
//对>v的右子数组再排序,此时j处在最左边的>v的位置上。
sort(a, j, hi);
}
//返回Tukey‘s ninther取样切分元素索引
private static int TukeysNintherIndex(Comparable[] a,int lo,int hi,int M)
{
//子数组少于4M个元素时,第一个元素作为切分元素
if((hi-lo+1)<4*M) return lo;
//子数组有4M个或以上元素时,取三个子数组中的中位数的中位数作为切分元素
////取第一个子数组
Integer[] firstArr={lo,lo+M/2,lo+M};
////按原数组的值对新数组进行排序。排序后的结果是原数组小中大值对应的索引
if(less(a[firstArr[1]],a[firstArr[0]])) exch(firstArr,0,1);
if(less(a[firstArr[2]],a[firstArr[1]])) exch(firstArr,1,2);
if(less(a[firstArr[1]],a[firstArr[0]])) exch(firstArr,0,1);
////取第二个子数组
Integer[] secondArr={(hi-lo)/2-M/2,(hi-lo)/2,(hi-lo)/2+M/2};
////按原数组的值对新数组进行排序。排序后的结果是原数组小中大值对应的索引
if(less(a[secondArr[1]],a[secondArr[0]])) exch(secondArr,0,1);
if(less(a[secondArr[2]],a[secondArr[1]])) exch(secondArr,1,2);
if(less(a[secondArr[1]],a[secondArr[0]])) exch(secondArr,0,1);
////取第三个子数组
Integer[] thirdArr={hi-M,hi-M/2,hi};
////按原数组的值对新数组进行排序。排序后的结果是原数组小中大值对应的索引
if(less(a[thirdArr[1]],a[thirdArr[0]])) exch(thirdArr,0,1);
if(less(a[thirdArr[2]],a[thirdArr[1]])) exch(thirdArr,1,2);
if(less(a[thirdArr[1]],a[thirdArr[0]])) exch(thirdArr,0,1);
////取三个数组中位数的中位数
Integer[] midArr={firstArr[1],secondArr[1],thirdArr[1]};
////按原数组的值对新数组进行排序。排序后的结果是原数组小中大值对应的索引
if(less(a[midArr[1]],a[midArr[0]])) exch(midArr,0,1);
if(less(a[midArr[2]],a[midArr[1]])) exch(midArr,1,2);
if(less(a[midArr[1]],a[midArr[0]])) exch(midArr,0,1);
return midArr[1];
}
private static void InsertSort(Comparable[] a,int lo,int hi)
{
for (int i=lo+1;i<hi+1;i++)
{
for (int j=i;j>0 && less(a[j],a[j-1]);j--)
exch(a,j,j-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];
StdOut.println(a.length);
for(int k=0;k<N;k++)
a[k]=StdRandom.random();
sort(a);
StdOut.println("isSorted=" +isSorted(a));
// show(a);
}
}