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); 
    
    } 
    
}