算法 - 堆排序

要点:将数组作为堆结构,利用大根堆根最大的性质,构建完就将根与未排序部分的末尾交换,逐步实现有序。

  1 import java.util.Random;
  2 
  3 public class HeapSort<T extends Comparable> {
  4 
  5     public void sort(T[] arr) {
  6         printArr(arr, " => 初始数组");
  7         for (int i = arr.length - 1; i > 0; i--) {
  8             // 构建大跟堆
  9             buildHeap(arr, 0, i);
 10             printArr(arr, " => 构建大根堆");
 11             // 最大值依次往后放,进行排序
 12             swap(arr, 0, i);
 13             printSign(0, i);
 14         }
 15     }
 16 
 17     /**
 18      * 从上向下构建大根堆
 19      * 很多大跟堆是从下向上构建的
 20      *
 21      * @param arr 原数组
 22      * @param rootIndex 根坐标
 23      * @param end 堆大小
 24      */
 25     private void buildHeap(T[] arr, int rootIndex, int end) {
 26         // 左子节点
 27         int leftChildIndex = rootIndex * 2 + 1;
 28         // 如果左子节点超出堆的大小就退出
 29         if (leftChildIndex > end) {
 30             return;
 31         }
 32         // 如果根比左子节点小就交换
 33         if (arr[rootIndex].compareTo(arr[leftChildIndex]) < 0) {
 34             swap(arr, rootIndex, leftChildIndex);
 35             // 交换可能会出现规则破坏,回溯一下
 36             backCheck(arr, rootIndex);
 37         }
 38         // 继续构建堆
 39         buildHeap(arr, leftChildIndex, end);
 40         // 右子节点
 41         int rightChildIndex = rootIndex * 2 + 2;
 42         // 如果右子节点超出堆的大小就退出
 43         if (rightChildIndex > end) {
 44             return;
 45         }
 46         // 如果根比右子节点大就交换
 47         if (arr[rootIndex].compareTo(arr[rightChildIndex]) < 0) {
 48             swap(arr, rootIndex, rightChildIndex);
 49             backCheck(arr, rootIndex);
 50         }
 51         // 继续构建堆
 52         buildHeap(arr, rightChildIndex, end);
 53     }
 54 
 55     /**
 56      * 向上追溯,查看是否乱序
 57      * 例:                                     树的坐标规律
 58      * 0,1,2,3,4 => 坐标                            0
 59      * 3,6,7,9,8 => 开始构建大跟堆                   1 2
 60      * 3,6       => 发现6比3大交换                  34 56
 61      * 6,3,9     => 发现9比3大交换
 62      * 6,9,3     => 如果在这里不追溯的话,就会出现不符合大跟堆规律的情况
 63      * 9,6,3     => 追溯发现根节点的父节点比根小,就交换,继续追溯,发现坐标已经为0了,就停下
 64      *
 65      * @param arr       数组
 66      * @param rootIndex 根坐标
 67      */
 68     private void backCheck(T[] arr, int rootIndex) {
 69         if (rootIndex == 0) {
 70             return;
 71         }
 72         int fatherIndex = (rootIndex - 1) / 2;
 73         if (arr[fatherIndex].compareTo(arr[rootIndex]) < 0) {
 74             swap(arr, fatherIndex, rootIndex);
 75         }
 76         backCheck(arr, fatherIndex);
 77     }
 78 
 79     private void swap(T[] arr, int a, int b) {
 80         T temp = arr[a];
 81         arr[a] = arr[b];
 82         arr[b] = temp;
 83     }
 84 
 85     private void printSign(int a, int b) {
 86         String[] arr = new String[]{" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "};
 87         arr[a] = "^";
 88         arr[b] = "^";
 89         for (String n : arr) {
 90             System.out.print(n);
 91         }
 92         System.out.println(" => 交换");
 93     }
 94 
 95     public void printArr(T[] arr, String message) {
 96         for (T n : arr) {
 97             System.out.print(n);
 98         }
 99         System.out.print(message);
100         System.out.println();
101     }
102 
103     public static void main(String[] args) {
104         int n = 11;
105         Integer[] arr = new Integer[n];
106         for (int i = 0; i < n; i++) {
107             arr[i] = new Random().nextInt(10);
108         }
109         HeapSort hs = new HeapSort();
110         hs.sort(arr);
111     }
112 
113     /**
114      * 92134594608 => 初始数组
115      * 98946152304 => 构建大根堆
116      * ^         ^ => 交换
117      * 96844152309 => 构建大根堆
118      * ^        ^  => 交换
119      * 84634150299 => 构建大根堆
120      * ^       ^   => 交换
121      * 64523140899 => 构建大根堆
122      * ^      ^    => 交换
123      * 53402146899 => 构建大根堆
124      * ^     ^     => 交换
125      * 43402156899 => 构建大根堆
126      * ^    ^      => 交换
127      * 42301456899 => 构建大根堆
128      * ^   ^       => 交换
129      * 31204456899 => 构建大根堆
130      * ^  ^        => 交换
131      * 20134456899 => 构建大根堆
132      * ^ ^         => 交换
133      * 10234456899 => 构建大根堆
134      * ^^          => 交换
135      *
136      * => 遍历次数:log(n) + log(n-1) + log(n-2) + ... + log(1)
137      * => 时间复杂度:O(nlogn)
138      * => 稳定性:不稳定
139      *
140      * 例:
141      * 0,1,2,3,5 => 坐标
142      * 7,1,3,3,2 => 原数组
143      * 7,1,3     => 3比1大交换
144      * 7,3,3,1,2 => 交换后的数组 => 3的顺序发生改变,因此不稳定
145      */
146 
147 }

 

posted @ 2020-04-29 18:02  御简  阅读(117)  评论(0编辑  收藏  举报