`
yangfuchao418
  • 浏览: 167132 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

java常用排序算法总结<一>【转】

    博客分类:
  • java
 
阅读更多

这篇排序文章从思想 理解 到实现,然后到整理,花了我几天的时间,现把它记录于此,希望对大家有一定的帮助,写的不好的请不要见笑,写错了的,请指出来我更正。最后如果对你有一定的帮助,请回贴支持一下哦^_^ !

申明: 排序算法思想来自互联网,代码自己实现,仅供参考。

  • 插入排序

直接插入排序、希尔排序

  • 选择排序

简单选择排序、堆排序

  • 交换排序

冒泡排序、快速排序

  • 归并排序
  • 基数排序

排序基类

Java代码  收藏代码
  1. package  sort;  
  2.   
  3. import  java.util.Arrays;  
  4. import  java.util.Comparator;  
  5. import  java.util.Random;  
  6.   
  7. /**  
  8.  * 排序接口,所有的排序算法都要继承该抽象类,并且要求数组中的  
  9.  * 元素要具有比较能力,即数组元素已实现了Comparable接口  
  10.  *   
  11.  * @author jzj  
  12.  * @date 2009-12-5  
  13.  *  
  14.  * @param <E>  
  15.  */   
  16. public   abstract   class  Sort<E  extends  Comparable<E>> {  
  17.   
  18.     public   final  Comparator<E> DEFAULT_ORDER =  new  DefaultComparator();  
  19.     public   final  Comparator<E> REVERSE_ORDER =  new  ReverseComparator();  
  20.   
  21.     /**  
  22.      * 排序算法,需实现,对数组中指定的元素进行排序  
  23.      * @param array 待排序数组  
  24.      * @param from 从哪里  
  25.      * @param end 排到哪里  
  26.      * @param c  
  27.      */   
  28.     public   abstract   void  sort(E[] array,  int  from,  int  end, Comparator<E> c);  
  29.   
  30.     /**  
  31.      * 对数组中指定部分进行排序  
  32.      * @param from 从哪里  
  33.      * @param len 排到哪里  
  34.      * @param array 待排序数组  
  35.      * @param c 比较器  
  36.      */   
  37.     public   void  sort( int  from,  int  len, E[] array, Comparator<E> c) {  
  38.         sort(array, 0 , array.length -  1 , c);  
  39.     }  
  40.   
  41.     /**  
  42.      * 对整个数组进行排序,可以使用自己的排序比较器,也可使用该类提供的两个比较器  
  43.      * @param array 待排序数组  
  44.      * @param c 比较器  
  45.      */   
  46.     public   final   void  sort(E[] array, Comparator<E> c) {  
  47.         sort(0 , array.length, array, c);  
  48.     }  
  49.   
  50.     /**  
  51.      * 对整个数组进行排序,采用默认排序比较器  
  52.      * @param array 待排序数组  
  53.      */   
  54.     public   final   void  sort(E[] array) {  
  55.         sort(0 , array.length, array,  this .DEFAULT_ORDER);  
  56.     }  
  57.   
  58.     //默认比较器(一般为升序,但是否真真是升序还得看E是怎样实现Comparable接口的)   
  59.     private   class  DefaultComparator  implements  Comparator<E> {  
  60.         public   int  compare(E o1, E o2) {  
  61.             return  o1.compareTo(o2);  
  62.         }  
  63.     }  
  64.   
  65.     //反序比较器,排序刚好与默认比较器相反   
  66.     private   class  ReverseComparator  implements  Comparator<E> {  
  67.         public   int  compare(E o1, E o2) {  
  68.             return  o2.compareTo(o1);  
  69.         }  
  70.     }  
  71.   
  72.     /**  
  73.      * 交换数组中的两个元素的位置  
  74.      * @param array 待交换的数组  
  75.      * @param i 第一个元素  
  76.      * @param j 第二个元素  
  77.      */   
  78.     protected   final   void  swap(E[] array,  int  i,  int  j) {  
  79.         if  (i != j) { //只有不是同一位置时才需交换   
  80.             E tmp = array[i];  
  81.             array[i] = array[j];  
  82.             array[j] = tmp;  
  83.         }  
  84.     }  
  85.   
  86.     /**  
  87.      * 数组元素后移  
  88.      * @param array 待移动的数组  
  89.      * @param startIndex 从哪个开始移  
  90.      * @param endIndex 到哪个元素止  
  91.      */   
  92.     protected   final   void  move(E[] array,  int  startIndex,  int  endIndex) {  
  93.         for  ( int  i = endIndex; i >= startIndex; i--) {  
  94.             array[i + 1 ] = array[i];  
  95.         }  
  96.     }  
  97.   
  98.     /**  
  99.      * 以指定的步长将数组元素后移,步长指定每个元素间的间隔  
  100.      * @param array 待排序数组  
  101.      * @param startIndex 从哪里开始移  
  102.      * @param endIndex 到哪个元素止  
  103.      * @param step 步长  
  104.      */   
  105.     protected   final   void  move(E[] array,  int  startIndex,  int  endIndex,  int  step) {  
  106.         for  ( int  i = endIndex; i >= startIndex; i -= step) {  
  107.             array[i + step] = array[i];  
  108.         }  
  109.     }  
  110.   
  111.     //测试方法   
  112.     @SuppressWarnings ( "unchecked" )  
  113.     public   static   final  <E  extends  Comparable<E>>  void  testSort(Sort<E> sorter, E[] array) {  
  114.   
  115.         if  (array ==  null ) {  
  116.             array = randomArray();  
  117.         }  
  118.         //为了第二次排序,需拷贝一份   
  119.         E[] tmpArr = (E[]) new  Comparable[array.length];  
  120.         System.arraycopy(array, 0 , tmpArr,  0 , array.length);  
  121.   
  122.         System.out.println("源 - "  + Arrays.toString(tmpArr));  
  123.   
  124.         sorter.sort(array, sorter.REVERSE_ORDER);  
  125.         System.out.println("降 - "  + Arrays.toString(array));  
  126.   
  127.         sorter.sort(tmpArr, sorter.DEFAULT_ORDER);  
  128.         System.out.println("升 - "  + Arrays.toString(tmpArr));  
  129.     }  
  130.   
  131.     //生成随机数组   
  132.     @SuppressWarnings ( "unchecked" )  
  133.     private   static  <E  extends  Comparable<E>> E[] randomArray() {  
  134.         Random r = new  Random(System.currentTimeMillis());  
  135.         Integer[] a = new  Integer[r.nextInt( 30 )];  
  136.         for  ( int  i =  0 ; i < a.length; i++) {  
  137.             a[i] = new  Integer(r.nextInt( 100 ));  
  138.         }  
  139.         return  (E[]) a;  
  140.     }  
  141. }   

插入排序

直接插入排序

 

一般直接插入排序的时间复杂度为O ( n^2 ) ,但是当数列基本有序时,如果按照有数列顺序排时,时间复杂度将改善到O( n ),另外,因直接插入排序算法简单,如果待排序列规模不很大时效率也较高。

 

在已经排好序的序列中查找待插入的元素的插入位置,并将待插入元素插入到有序列表中的过程。
 
将数组分成两部分,初始化时,前部分数组为只有第一个元素,用来存储已排序元素,我们这里叫 arr1 ;后部分数组的元素为除第一个元素的所有元素,为待排序或待插入元素,我们这里叫 arr2 。
排序时使用二层循环:第一层对 arr2 进行循环,每次取后部分数组(待排序数组)里的第一个元素(我们称为待排序元素或称待插入元素) e1 ,然后在第二层循环中对 arr1 (已排好序的数组)从第一个元素往后进行循环,查到第一个大于待插入元素(如果是升序排列)或第一个小于待插入元素(如果是降序排列) e2 ,然后对 arr1 从 e2 元素开始往后的所有元素向后移,最后把 e1 插入到原来 e2 所在的位置。这样反复地对 arr2 进行循环,直到 arr2 中所有的待插入的元素都插入到 arr1 中。

Java代码  收藏代码
  1. package  sort;  
  2.   
  3. import  java.util.Comparator;  
  4.   
  5. /**  
  6.  *  直接插入排序算法  
  7.  * @author jzj  
  8.  * @date 2009-12-5  
  9.  *   
  10.  * @param <E>  
  11.  */   
  12. public   class  InsertSort<E  extends  Comparable<E>>  extends  Sort<E> {  
  13.   
  14.     /**  
  15.      * 排序算法的实现,对数组中指定的元素进行排序  
  16.      * @param array 待排序的数组  
  17.      * @param from 从哪里开始排序  
  18.      * @param end 排到哪里  
  19.      * @param c 比较器  
  20.      */   
  21.     public   void  sort(E[] array,  int  from,  int  end, Comparator<E> c) {  
  22.   
  23.         /*  
  24.          * 第一层循环:对待插入(排序)的元素进行循环  
  25.          * 从待排序数组断的第二个元素开始循环,到最后一个元素(包括)止  
  26.          */   
  27.         for  ( int  i = from +  1 ; i <= end; i++) {  
  28.             /*  
  29.              * 第二层循环:对有序数组进行循环,且从有序数组最第一个元素开始向后循环  
  30.              * 找到第一个大于待插入的元素  
  31.              * 有序数组初始元素只有一个,且为源数组的第一个元素,一个元素数组总是有序的  
  32.              */   
  33.             for  ( int  j =  0 ; j < i; j++) {  
  34.                 E insertedElem = array[i];//待插入到有序数组的元素   
  35.                 //从有序数组中最一个元素开始查找第一个大于待插入的元素   
  36.                 if  (c.compare(array[j], insertedElem) >  0 ) {  
  37.                     //找到插入点后,从插入点开始向后所有元素后移一位   
  38.                     move(array, j, i - 1 );  
  39.                     //将待排序元素插入到有序数组中   
  40.                     array[j] = insertedElem;  
  41.                     break ;  
  42.                 }  
  43.             }  
  44.         }  
  45.   
  46.         //=======以下是java.util.Arrays的插入排序算法的实现   
  47.         /*  
  48.          * 该算法看起来比较简洁一j点,有点像冒泡算法。  
  49.          * 将数组逻辑上分成前后两个集合,前面的集合是已经排序好序的元素,而后面集合为待排序的  
  50.          * 集合,每次内层循从后面集合中拿出一个元素,通过冒泡的形式,从前面集合最后一个元素开  
  51.          * 始往前比较,如果发现前面元素大于后面元素,则交换,否则循环退出  
  52.          *   
  53.          * 总感觉这种算术有点怪怪,既然是插入排序,应该是先找到插入点,而后再将待排序的元素插  
  54.          * 入到的插入点上,那么其他元素就必然向后移,感觉算法与排序名称不匹,但返过来与上面实  
  55.          * 现比,其实是一样的,只是上面先找插入点,待找到后一次性将大的元素向后移,而该算法却  
  56.          * 是走一步看一步,一步一步将待排序元素往前移  
  57.          */   
  58.         /*  
  59.         for (int i = from; i <= end; i++) {  
  60.             for (int j = i; j > from && c.compare(array[j - 1], array[j]) > 0; j--) {  
  61.                 swap(array, j, j - 1);  
  62.             }  
  63.         }  
  64.         */   
  65.     }  
  66.   
  67.     /**  
  68.      * 测试  
  69.      * @param args  
  70.      */   
  71.     public   static   void  main(String[] args) {  
  72.         Integer[] intgArr = { 5 9 1 4 1 2 6 3 8 0 7  };  
  73.         InsertSort<Integer> insertSort = new  InsertSort<Integer>();  
  74.         Sort.testSort(insertSort, intgArr);  
  75.         Sort.testSort(insertSort, null );  
  76.     }  
  77. }  

插入排序算法对于大数组,这种算法非常慢。但是对于小数组,它比其他算法快。其他算法因为待的数组元素很少,反而使得效率降低。在Java集合框架 中,排序都是借助于java.util.Arrays来完成的,其中排序算法用到了插入排序、快速排序、归并排序。插入排序用于元素个数小于7的子数组排 序,通常比插入排序快的其他排序方法,由于它们强大的算法是针对大数量数组设计的,所以元素个数少时速度反而慢。

希尔排序

希尔思想介绍

希尔算法的本质是缩小增量排序,是对直接插入排序算法的改进。一般直接插入排序的时间复杂度为O ( n^2 ) ,但是当数列基本有序时,如果按照有数列顺序排时,时间复杂度将改善到O( n ),另外,因直接插入排序算法简单,如果待排序列规模不很大时效率也较高,Shell 根据这两点分析结果进行了改进,将待排记录序列以一定的增量间隔h 分割成多个子序列,对每个子序列分别进行一趟直接插入排序, 然后逐步减小分组的步长h ,对于每一个步长h 下的各个子序列进行同样方法的排序,直到步长为1 时再进行一次整体排序。
因为不管记录序列多么庞大,关键字多么混乱,在先前较大的分组步长h 下每个子序列的规模都不大,用直接插入排序效率都较高。 尽管在随后的步长h 递减分组中子序列越来越大,但由于整个序列的有序性也越来越明显,则排序效率依然较高。这种改进抓住了直接插入排序的两点本质,大大提高了它的时间效率。

希尔增量研究

综上所述:

(1) 希尔排序的核心是以某个增量h 为步长跳跃分组进行插入排序,由于分组的步长h 逐步缩小,所以也叫“缩小增量排序”插入排序。其关键是如何选取分组的步长序列ht ,. . . , hk ,. . . , h1 , h0 才能使得希尔方法的时间效率最高;

(2) 待排序列记录的个数n 、跳跃分组步长逐步减小直到为1时所进行的扫描次数T、增量的和、记录关键字比较的次数以及记录移动的次数或各子序列中的反序数等因素都影响希尔算法的时间复杂度:其中记录关键字比较的次数是重要因素,它主要取决于分组步长序列的选择;

(3) 希尔方法是一种不稳定排序算法,因为其排序过程中各趟的步长不同,在第k 遍用hk作为步长排序之后,第k +1 遍排序时可能会遇到多个逆序存在,影响排序的稳定性。

 

试验结果表明,SHELL 算法的时间复杂度受增量序列的影响明显大于其他因素,选取恰当的增量序列能明显提高排序的时间效率,我们认为第k 趟排序扫描的增量步长为 2^k - 1 ,即增量序列为. . . 2^k - 1 ,. . . ,15 ,7 ,3 ,1时较为理想,但它并不是唯一的最佳增量序列,这与其关联函数目前尚无确定解的理论结果是一致的。


Java代码  收藏代码
  1. package  sort;  
  2.   
  3. import  java.util.Comparator;  
  4.   
  5. /**  
  6.  * 希尔排序算法  
  7.  * @author jzj  
  8.  * @date 2009-12-5  
  9.  *   
  10.  * @param <E>  
  11.  */   
  12. public   class  ShelltSort<E  extends  Comparable<E>>  extends  Sort<E> {  
  13.   
  14.     /**  
  15.      * 排序算法的实现,对数组中指定的元素进行排序  
  16.      * @param array 待排序的数组  
  17.      * @param from 从哪里开始排序  
  18.      * @param end 排到哪里  
  19.      * @param c 比较器  
  20.      */   
  21.     public   void  sort(E[] array,  int  from,  int  end, Comparator<E> c) {  
  22.         //初始步长,实质为每轮的分组数   
  23.         int  step = initialStep(end - from +  1 );  
  24.   
  25.         //第一层循环是对排序轮次进行循环。(step + 1) / 2 - 1 为下一轮步长值   
  26.         for  (; step >=  1 ; step = (step +  1 ) /  2  -  1 ) {  
  27.             //对每轮里的每个分组进行循环   
  28.             for  ( int  groupIndex =  0 ; groupIndex < step; groupIndex++) {  
  29.   
  30.                 //对每组进行直接插入排序   
  31.                 insertSort(array, groupIndex, step, end, c);  
  32.             }  
  33.         }  
  34.     }  
  35.   
  36.     /**  
  37.      * 直接插入排序实现  
  38.      * @param array 待排序数组  
  39.      * @param groupIndex 对每轮的哪一组进行排序  
  40.      * @param step 步长  
  41.      * @param end 整个数组要排哪个元素止  
  42.      * @param c 比较器  
  43.      */   
  44.     private   void  insertSort(E[] array,  int  groupIndex,  int  step,  int  end, Comparator<E> c) {  
  45.         int  startIndex = groupIndex; //从哪里开始排序   
  46.         int  endIndex = startIndex; //排到哪里   
  47.         /*  
  48.          * 排到哪里需要计算得到,从开始排序元素开始,以step步长,可求得下元素是否在数组范围内,  
  49.          * 如果在数组范围内,则继续循环,直到索引超现数组范围  
  50.          */   
  51.         while  ((endIndex + step) <= end) {  
  52.             endIndex += step;  
  53.         }  
  54.   
  55.         // i为每小组里的第二个元素开始   
  56.         for  ( int  i = groupIndex + step; i <= end; i += step) {  
  57.             for  ( int  j = groupIndex; j < i; j += step) {  
  58.                 E insertedElem = array[i];  
  59.                 //从有序数组中最一个元素开始查找第一个大于待插入的元素   
  60.                 if  (c.compare(array[j], insertedElem) >=  0 ) {  
  61.                     //找到插入点后,从插入点开始向后所有元素后移一位   
  62.                     move(array, j, i - step, step);  
  63.                     array[j] = insertedElem;  
  64.                     break ;  
  65.                 }  
  66.             }  
  67.         }  
  68.     }  
  69.   
  70.     /**  
  71.      * 根据数组长度求初始步长  
  72.      *   
  73.      * 我们选择步长的公式为:2^k-1,2^(k-1)-1,...,15,7,3,1 ,其中2^k 减一即为该步长序列,k  
  74.      * 为排序轮次  
  75.      *   
  76.      * 初始步长:step = 2^k-1   
  77.      * 初始步长约束条件:step < len - 1 初始步长的值要小于数组长度还要减一的值(因  
  78.      * 为第一轮分组时尽量不要分为一组,除非数组本身的长度就小于等于4)  
  79.      *   
  80.      * 由上面两个关系试可以得知:2^k - 1 < len - 1 关系式,其中k为轮次,如果把 2^k 表 达式  
  81.      * 转换成 step 表达式,则 2^k-1 可使用 (step + 1)*2-1 替换(因为 step+1 相当于第k-1  
  82.      * 轮的步长,所以在 step+1 基础上乘以 2 就相当于 2^k 了),即步长与数组长度的关系不等式为  
  83.      * (step + 1)*2 - 1 < len -1  
  84.      *   
  85.      * @param len 数组长度  
  86.      * @return  
  87.      */   
  88.     private   static   int  initialStep( int  len) {  
  89.         /*  
  90.          * 初始值设置为步长公式中的最小步长,从最小步长推导出最长初始步长值,即按照以下公式来推:  
  91.          * 1,3,7,15,...,2^(k-1)-1,2^k-1  
  92.          * 如果数组长度小于等于4时,步长为1,即长度小于等于4的数组不且分组,此时直接退化为直接插  
  93.          * 入排序  
  94.          */   
  95.         int  step =  1 ;  
  96.   
  97.         //试探下一个步长是否满足条件,如果满足条件,则步长置为下一步长   
  98.         while  ((step +  1 ) *  2  -  1  < len -  1 ) {  
  99.             step = (step + 1 ) *  2  -  1 ;  
  100.         }  
  101.   
  102.         System.out.println("初始步长 - "  + step);  
  103.         return  step;  
  104.     }  
  105.   
  106.     /**  
  107.      * 测试  
  108.      * @param args  
  109.      */   
  110.     public   static   void  main(String[] args) {  
  111.         Integer[] intgArr = { 5 9 1 4 8 2 6 3 7 10  };  
  112.         ShelltSort<Integer> shellSort = new  ShelltSort<Integer>();  
  113.         Sort.testSort(shellSort, intgArr);  
  114.         Sort.testSort(shellSort, null );  
  115.     }  
  116. }   

选择排序

简单选择排序

每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
选择排序不像冒泡排序算法那样先并不急于调换位置,第一轮(k=1)先从array[k]开始逐个检查,看哪个数最小就记下该数所在的位置于 minlIndex中,等一轮扫描完毕,如果找到比array[k-1]更小的元素,则把array[minlIndex]和a[k-1]对调,这时 array[k]到最后一个元素中最小的元素就换到了array[k-1]的位置。 如此反复进行第二轮、第三轮…直到循环至最后一元素

Java代码  收藏代码
  1. package  sort;  
  2.   
  3. import  java.util.Comparator;  
  4.   
  5. /**  
  6.  * 简单选择排序算法  
  7.  * @author jzj  
  8.  * @date 2009-12-5  
  9.  *   
  10.  * @param <E>  
  11.  */   
  12. public   class  SelectSort<E  extends  Comparable<E>>  extends  Sort<E> {  
  13.   
  14.     /**  
  15.      * 排序算法的实现,对数组中指定的元素进行排序  
  16.      * @param array 待排序的数组  
  17.      * @param from 从哪里开始排序  
  18.      * @param end 排到哪里  
  19.      * @param c 比较器  
  20.      */   
  21.     public   void  sort(E[] array,  int  from,  int  end, Comparator<E> c) {  
  22.         int  minlIndex; //最小索引   
  23.         /*  
  24.          * 循环整个数组(其实这里的上界为 array.length - 1 即可,因为当 i= array.length-1  
  25.          * 时,最后一个元素就已是最大的了,如果为array.length时,内层循环将不再循环),每轮假设  
  26.          * 第一个元素为最小元素,如果从第一元素后能选出比第一个元素更小元素,则让让最小元素与第一  
  27.          * 个元素交换   
  28.          */   
  29.         for  ( int  i = from; i <= end; i++) {  
  30.             minlIndex = i;//假设每轮第一个元素为最小元素   
  31.             //从假设的最小元素的下一元素开始循环   
  32.             for  ( int  j = i +  1 ; j <= end; j++) {  
  33.                 //如果发现有比当前array[smallIndex]更小元素,则记下该元素的索引于smallIndex中   
  34.                 if  (c.compare(array[j], array[minlIndex]) <  0 ) {  
  35.                     minlIndex = j;  
  36.                 }  
  37.             }  
  38.   
  39.             //先前只是记录最小元素索引,当最小元素索引确定后,再与每轮的第一个元素交换   
  40.             swap(array, i, minlIndex);  
  41.         }  
  42.     }  
  43.   
  44.     /**  
  45.      * 测试  
  46.      * @param args  
  47.      */   
  48.     public   static   void  main(String[] args) {  
  49.         Integer[] intgArr = { 5 9 1 4 1 2 6 3 8 0 7  };  
  50.         SelectSort<Integer> insertSort = new  SelectSort<Integer>();  
  51.         Sort.testSort(insertSort, intgArr);  
  52.         Sort.testSort(insertSort, null );  
  53.     }  
  54. }   

堆排序

堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

【例】关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆,其对应的完全二叉树分别如小根堆示例和大根堆示例所示:

根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小顶堆。
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大顶堆。

堆是一种完全二叉树,一般使用数组来实现。堆排序也是一种选择性的排序,每次选择第i大的元素。

 

另外排序过程中借助了堆结构,堆就是一种完全二叉树,所以这里先要熟悉要用的二叉树几个性质:

N(N>1)个节点的的完全二叉树从层次从左自右编号,最后一个分枝节点(非叶子节点)的编号为 N/2 取整。

且对于编号 i(1<=i<=N)有:父节点为 i/2 向下取整;若2i>N,则节点i没有左孩子,否则其左孩子为2i;若2i+1>N,则没有右孩子,否则其右孩子为2i+1。

注,这里使用完全二叉树只是为了好描述算法,它只是一种逻辑结构,真真在实现时我们还是使用数组来存储这棵二叉树的,因为完全二叉树完全可以使用数组来存储。

 

算法描述:

堆排序其实最主要的两个过程:第一步,创建初始堆;第二步,交换根节点与最后一个非叶子节

第一步实现 :从最后一个非叶子节点为开始向前循环每个会支节点,比较每个分支节点与他左右子节点,如果其中某个子节点比父节点大,则与父节点交换,交换后原父节点可 能还小于原子节点的子节点,所以还需对原父节点进行调整,使用原父节点继续下沉,直到没有子节点或比左右子节点都大为止,调用过程可通过递归完成。当某个 非叶子节点调整完毕后,再处理下一个非叶子节点,直到根节点也调整完成,这里初始堆就创建好了,这里我们创建的是大顶堆,即大的元素向树的根浮,这样排序 最后得到的结果为升序,因为最大的从树中去掉,并从数组最后往前存放。

第二步实现 :将树中的最后一个元素与堆顶元素进行交换,并从树中去掉最后叶子节点。交换后再按创建初始堆的算法调整根节点,如此下去直到树中只有一个节点为止。

 

Java代码  收藏代码
  1. package  sort;  
  2.   
  3. import  java.util.Comparator;  
  4.   
  5. public   class  HeapSort<E  extends  Comparable<E>>  extends  Sort<E> {  
  6.   
  7.     /**  
  8.      * 排序算法的实现,对数组中指定的元素进行排序  
  9.      * @param array 待排序的数组  
  10.      * @param from 从哪里开始排序  
  11.      * @param end 排到哪里  
  12.      * @param c 比较器  
  13.      */   
  14.     public   void  sort(E[] array,  int  from,  int  end, Comparator<E> c) {  
  15.         //创建初始堆   
  16.         initialHeap(array, from, end, c);  
  17.   
  18.         /*  
  19.          * 对初始堆进行循环,且从最后一个节点开始,直接树只有两个节点止  
  20.          * 每轮循环后丢弃最后一个叶子节点,再看作一个新的树  
  21.          */   
  22.         for  ( int  i = end - from +  1 ; i >=  2 ; i--) {  
  23.             //根节点与最后一个叶子节点交换位置,即数组中的第一个元素与最后一个元素互换   
  24.             swap(array, from, i - 1 );  
  25.             //交换后需要重新调整堆   
  26.             adjustNote(array, 1 , i -  1 , c);  
  27.         }  
  28.   
  29.     }  
  30.   
  31.     /**  
  32.      * 初始化堆  
  33.      * 比如原序列为:7,2,4,3,12,1,9,6,8,5,10,11  
  34.      * 则初始堆为:1,2,4,3,5,7,9,6,8,12,10,11  
  35.      * @param arr 排序数组  
  36.      * @param from 从哪  
  37.      * @param end 到哪  
  38.      * @param c 比较器  
  39.      */   
  40.     private   void  initialHeap(E[] arr,  int  from,  int  end, Comparator<E> c) {  
  41.         int  lastBranchIndex = (end - from +  1 ) /  2 ; //最后一个非叶子节点   
  42.         //对所有的非叶子节点进行循环 ,且从最一个非叶子节点开始   
  43.         for  ( int  i = lastBranchIndex; i >=  1 ; i--) {  
  44.             adjustNote(arr, i, end - from + 1 , c);  
  45.         }  
  46.     }  
  47.   
  48.     /**  
  49.      * 调整节点顺序,从父、左右子节点三个节点中选择一个最大节点与父节点转换  
  50.      * @param arr 待排序数组  
  51.      * @param parentNodeIndex 要调整的节点,与它的子节点一起进行调整  
  52.      * @param len 树的节点数  
  53.      * @param c 比较器  
  54.      */   
  55.     private   void  adjustNote(E[] arr,  int  parentNodeIndex,  int  len, Comparator<E> c) {  
  56.         int  minNodeIndex = parentNodeIndex;  
  57.         //如果有左子树,i * 2为左子节点索引    
  58.         if  (parentNodeIndex *  2  <= len) {  
  59.             //如果父节点小于左子树时    
  60.             if  (c.compare(arr[parentNodeIndex -  1 ], arr[parentNodeIndex *  2  -  1 ]) <  0 ) {  
  61.                 minNodeIndex = parentNodeIndex * 2 ; //记录最大索引为左子节点索引    
  62.             }  
  63.   
  64.             // 只有在有或子树的前提下才可能有右子树,再进一步断判是否有右子树    
  65.             if  (parentNodeIndex *  2  +  1  <= len) {  
  66.                 //如果右子树比最大节点更大    
  67.                 if  (c.compare(arr[minNodeIndex -  1 ], arr[(parentNodeIndex *  2  +  1 ) -  1 ]) <  0 ) {  
  68.                     minNodeIndex = parentNodeIndex * 2  +  1 ; //记录最大索引为右子节点索引    
  69.                 }  
  70.             }  
  71.         }  
  72.   
  73.         //如果在父节点、左、右子节点三都中,最大节点不是父节点时需交换,把最大的与父节点交换,创建大顶堆   
  74.         if  (minNodeIndex != parentNodeIndex) {  
  75.             swap(arr, parentNodeIndex - 1 , minNodeIndex -  1 );  
  76.             //交换后可能需要重建堆,原父节点可能需要继续下沉   
  77.             if  (minNodeIndex *  2  <= len) { //是否有子节点,注,只需判断是否有左子树即可知道   
  78.                 adjustNote(arr, minNodeIndex, len, c);  
  79.             }  
  80.         }  
  81.     }  
  82.   
  83.     /**   
  84.     * 测试   
  85.     * @param args   
  86.     */   
  87.     public   static   void  main(String[] args) {  
  88.         Integer[] intgArr = { 7 2 4 3 12 1 9 6 8 5 10 11  };  
  89.         HeapSort<Integer> sort = new  HeapSort<Integer>();  
  90.         HeapSort.testSort(sort, intgArr);  
  91.         HeapSort.testSort(sort, null );  
  92.     }  
  93.   
  94. }
原文:http://jiangzhengjun.iteye.com/blog/547734
 

 

 

分享到:
评论

相关推荐

    常用排序算法java演示

    本文将深入探讨标题"常用排序算法java演示"中涉及的知识点,包括排序算法的原理、Java实现方式以及其在实际应用中的图形演示。 首先,让我们逐一了解几种常见的排序算法: 1. **冒泡排序(Bubble Sort)**:这是一...

    经典算法问题的java实现<一>

    在本资源中,我们关注的是"经典算法问题的java实现&lt;一&gt;",这通常涉及到计算机科学中的基础算法,特别是那些用Java编程语言实现的。这些算法是解决各种计算问题的关键,包括排序、搜索、图论、动态规划等。Java作为一...

    常用排序算法总结(含Java代码)

    冒泡排序和快速排序是两种基础但广泛使用的数据排序算法。冒泡排序由于其简单直观的特性,易于理解和实现,而快速排序则以其较高的效率在数据量较大时展现出优势。 首先,让我们来看冒泡排序算法。冒泡排序通过重复...

    Java常用排序算法源码

    在编程领域,排序算法是计算机科学中的核心概念,特别是在Java这样的高级编程语言中。排序算法是用来组织和优化数据结构的关键工具,使得数据按照特定规则(如升序或降序)排列。以下是对Java中几种常见排序算法的...

    AIC的Java课程1-6章

    第3版 机械工业出版社&lt;br&gt; 教学内容和要求&lt;br&gt;知识点 重要程度 使用频度 难度&lt;br&gt;Java 入门 高 中 易&lt;br&gt;变量和运算符 高 高 中&lt;br&gt;控制结构 高 高 易&lt;br&gt;数组 高 高 中&lt;br&gt;方法 很高 高 中&lt;br&gt;封装 很高 很高 难...

    Java常用排序算法&程序员必须掌握的8大排序算法+二分法查找

    本资料包聚焦于"Java常用排序算法"和"程序员必须掌握的8大排序算法",并深入探讨了"二分法查找"这一高效搜索技术。 首先,我们来看八大排序算法。这些算法包括: 1. **冒泡排序**:最简单的排序方法,通过不断交换...

    Java常用排序算法

    以下是关于"Java常用排序算法"的详细解释: 1. 插入排序(Insertion Sort) 插入排序是一种简单直观的排序算法,它的工作原理类似于我们日常生活中整理扑克牌的过程。算法分为两个阶段:遍历待排序的数组,将每个...

    Java常用排序算法程序员必须掌握的8大排序算法

    冒泡排序--Java常用排序算法程序员必须掌握的8大排序算法

    Java常用8大排序算法

    ### Java常用八大排序算法详解 #### 一、直接插入排序 **基本思想:** 直接插入排序的基本思路是在要排序的一组数中,假设前面 (n-1) [n&gt;=2] 个数已经排好顺序,现在要把第 n 个数插入到前面的有序数列中,使得这 ...

    java 常用排序算法

    总结来说,Java中常用的排序算法如Shell排序和快速排序各有特点,Shell排序适用于处理大型数据,而快速排序通常在平均情况下具有较高的效率。在实际应用中,根据数据特性选择合适的排序算法至关重要,以实现最佳性能...

    详解Java常用排序算法-选择排序

    详解Java常用排序算法-选择排序 选择排序(Selection Sort)是一种简单的排序算法,它的基本思想是每次从待排序的元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的元素排完。 选择...

    详解Java常用排序算法-归并排序

    Java常用排序算法-归并排序 归并排序是一种分治思想的排序算法,其基本思想是将待排序的数组分成若干个子序列,每个子序列都是有序的,然后再将子序列合并成一个有序的数组。这种算法的时间复杂度为O(n log n),...

    详解Java常用排序算法-插入排序

    Java排序算法 - 插入排序 插入排序(Insertion Sort)是一种简单的排序算法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。该算法的实现非常简单,但其时间复杂度...

    详解Java常用排序算法-快速排序

    快速排序(Quick Sort)是一种分治思想的排序算法,它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后再分别对这两部分记录继续进行排序,以达到...

    Java常用排序算法程序员必须掌握的8大排序算法Java开

    学习资料如"Java常用排序算法程序员必须掌握的8大排序算法Java开发Java经验技巧共16页.pdf"可以提供详细的讲解和示例,帮助你更好地理解和实践这些算法。同时,这些排序算法不仅限于Java,也广泛应用于Python、C语言...

    详解Java常用排序算法-基数排序

    Java 基数排序算法详解 基数排序(Radix Sort)是一种非比较排序算法,它的基本思想是将待排序的数组按照位数(个位、十位、百位)进行划分,然后依次对每个位上的数字进行排序,最终得到有序的数组。基数排序的...

Global site tag (gtag.js) - Google Analytics