【排序算法】03堆排序

接上文: 【排序算法】02归并排序

 

堆排序是对选择排序的改进。

--- 适用于:
    基本有序(正序或反序)的情况。

--- 堆的定义:
对于数列{r[1], r[2], ... , r[n]},
小顶堆:满足r[i] <= r[2i] && r[i] <= r[2i+1],r[1]最小
大顶堆:满足r[i] >= r[2i] && r[i] >= r[2i+1],r[1]最大
可将堆看成一颗完全二叉树,按顺序分配的方式进行存储!
r[1]为堆顶,r[n]为堆底。

  时间O(n*log(n))
   空间O(1)
  
--- 堆排序核心:     
在堆顶r[1]和堆底r[n]交换之后,如何将序列{r[1], ..., r[n-1]}重新调整为堆。

--- 堆排序思路:
先建一个初始大顶堆,即可选得一个最大的记录,将其与序列中最后一个记录交换,
之后继续对序列前n-1个记录进行筛选。

 

向工具类ArraySorterUtils中添加堆排序的实现,代码如下:

 1 package org.liws1.sort;
 2 
 3 import java.util.Arrays;
 4 import java.util.Comparator;
 5 
 6 /**
 7  * 数组的排序,这里统一做升序排序
 8  */
 9 public class ArraySorterUtils {
10 
11     private static <T> void swap(T[] datas, int i, int j) {
12         if (i == j) return;
13         T temp = datas[i];
14         datas[i] = datas[j];
15         datas[j] = temp;
16     }
17     
18     public static class HeapSorter implements IArraySorter {
19         
20         @Override public <T extends Comparable<T>> void sort(T[] list) {
21             // 1、建立初始大顶堆,并交换堆顶和堆底的记录
22             buildFirstHeap(list);
23             int len = list.length;
24             swap(list, 0, len - 1);
25             // 2、反复调整
26             for (int i = len - 2; i > 0; i--) {
27                 heapAdjust(list, 0, i);
28                 swap(list, 0, i);
29             }
30         }
31 
32         @Override public <T> void sort(T[] list, Comparator<T> comp) {
33             // 省略实现,跟sort(T[] list)没差
34         }
35         
36         /** 
37          * 将一个无序序列调整为大顶堆[升序用大顶堆]<br>
38          * 是一个"自下而上"的过程。
39          * 
40          * 思路:
41          * 对于有n个元素的完全二叉树,[n/2, n-1]均为叶子节点,每个叶子节点都是堆,
42          * 所以只要从最后一个非叶子节点[n/2-1]开始,到根节点位置,依次调用heapAdjust
43          * ,即可将一个无序序列调整为一个大顶堆。
44          */
45         private static <T extends Comparable<T>> void buildFirstHeap(T[] datas) {
46             int len = datas.length;
47             for (int i = len / 2 - 1; i >= 0; i--) {
48                 heapAdjust(datas, i, len - 1);
49             }
50         }
51         
52         /**
53          * 在datas[start]不是大顶堆但是其'左子树'和'右子树'都为大顶堆的情况下,
54          * 将datas[start ... end]调整为大顶堆。
55          * 
56          * 注意:x的子节点为2x+1和2x+2 !
57          * @param datas 
58          * @param start
59          * @param end
60          */
61         private static <T extends Comparable<T>> void heapAdjust(T[] datas, int start, int end){
62             int father = start;
63             int largerKid = 2 * father + 1;
64             while (largerKid <= end) {     // 有左子节点
65                 // 1、确定较大子节点
66                 if (largerKid < end) {    // 且有右子节点
67                     if (datas[largerKid].compareTo(datas[largerKid + 1]) < 0) {
68                         largerKid = largerKid + 1;
69                     }
70                 }
71                 // 2、如果父节点记录小于较大子节点记录,则做交换;
72                 //    否则表示已调整为大顶堆,终止循环。
73                 if (datas[father].compareTo(datas[largerKid]) < 0) {
74                     swap(datas, father, largerKid);
75 
76                     father = largerKid;
77                     largerKid = 2 * largerKid + 1;
78                 } else {
79                     break;
80                 }
81             }
82         }
83 
84     }
85     
86 }

 

测试代码如下:

 1 package org.liws1.sort;
 2 
 3 import java.util.Arrays;
 4 import org.junit.Test;
 5 
 6 public class _Test {
 7 
 8     private Integer[] datas = { 30, 1, 29, 2, 28, 3, 27, 4, 26, 5, 25, 6, 24, 7,
 9             23, 8, 22, 9, 21, 10, 20, 19, 15, 18, 12, 17, 11, 16, 14, 13 };
10 
11     @Test public void testHeap(){
12         new ArraySorterUtils.HeapSorter().sort(datas);
13         System.out.println(Arrays.toString(datas));
14     } // out:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
15 }

 

 

 

posted @ 2019-05-06 01:02  渣渣伟  阅读(255)  评论(0编辑  收藏  举报
--->