【排序算法】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 }