最大堆性质:任一节点比其左右节点值都大。
最小堆性质:任一节点比其左右节点值都小。
应用:解决TopK问题。
TopK问题是指从大量数据(源数据)中获取最大(或最小)的K个数据。
1 package com.sunshine.AlgorithmTemplate; 2 3 import org.junit.Test; 4 5 import java.util.Arrays; 6 import java.util.Random; 7 8 public class HeapTemplate2 { 9 10 /*** 11 * 父节点 12 */ 13 private int parent(int pos) { 14 return (pos - 1) / 2; 15 } 16 17 /*** 18 * 左孩子 19 */ 20 private int leftChild(int pos) { 21 return pos * 2 + 1; 22 } 23 24 /*** 25 * 右孩子 26 */ 27 private int rightChild(int pos) { 28 return pos * 2 + 2; 29 } 30 31 /*** 32 * 值交换 33 */ 34 private void swap(int[] heap, int i, int j) { 35 int tmp = heap[i]; 36 heap[i] = heap[j]; 37 heap[j] = tmp; 38 } 39 40 /*** 41 * 按树结构打印 42 */ 43 public void printHeap(int[] heap, int size) { 44 int a = 2; 45 for (int i = 0; i < size; i++) { 46 System.out.print(heap[i] + " "); 47 if (i == a - 2) { 48 a *= 2; 49 System.out.println(); 50 } 51 } 52 System.out.println(); 53 } 54 55 /*** 56 * 打印排序后数组 57 */ 58 public void printArrSorted(int[] arr) { 59 Arrays.sort(arr); 60 for (int i : arr) { 61 System.out.print(i + " "); 62 } 63 System.out.println(); 64 } 65 66 /*** 67 * 打印数组 68 */ 69 public void printArr(int[] arr) { 70 for (int i : arr) { 71 System.out.print(i + " "); 72 } 73 System.out.println(); 74 } 75 76 /*** 77 * 前N个最大值 78 */ 79 @Test 80 public void topMaxN() { 81 final int len = 20; 82 final int topN = 7; 83 int[] heap = new int[len]; 84 Random random = new Random(); 85 for (int i = 0; i < len; i++) { 86 heap[i] = random.nextInt(100); 87 } 88 89 //创建topN最小堆 90 createHeapMin(heap, topN); 91 //遍历数组,并维护topN最小堆 92 findTopMaxN(heap, topN); 93 94 95 printHeap(heap, topN); 96 printArr(heap); 97 printArrSorted(heap); 98 System.out.println(); 99 System.out.println(heap[heap.length - topN]); 100 } 101 102 103 /*** 104 * 遍历数据组,并维护最小堆 105 */ 106 private void findTopMaxN(int[] heap, int topN) { 107 for (int i = topN; i < heap.length; i++) { 108 adjustDownTopMaxN(heap, topN, i); 109 } 110 } 111 112 /*** 113 * 创建最小堆 114 */ 115 private void createHeapMin(int[] heap, int size) { 116 for (int i = 1; i < size; i++) { 117 adjustUpMin(heap, i); 118 } 119 } 120 121 /*** 122 * 向上调整新加入节点位置 123 */ 124 private void adjustUpMin(int[] heap, int pos) { 125 while (parent(pos) >= 0 && heap[parent(pos)] > heap[pos]) { 126 int parent = parent(pos); 127 swap(heap, parent, pos); 128 pos = parent; 129 } 130 } 131 132 /*** 133 * 向下调整新加入节点位置,并维护最小堆 134 */ 135 private void adjustDownTopMaxN(int[] heap, int topN, int pos) { 136 //比topN中最小的还要小直接返回 137 if (heap[0] >= heap[pos]) { 138 return; 139 } 140 swap(heap, 0, pos); 141 pos = 0; 142 while (leftChild(pos) < topN) { 143 int child = leftChild(pos); 144 //判断左右孩子的大小,child代表较小的孩子 145 if (child + 1 < topN && heap[child + 1] < heap[child]) { 146 child++; 147 } 148 //新节点比较小的孩子都小,说明找到对应位置,直接跳出勋魂 149 if (heap[child] >= heap[pos]) { 150 break; 151 } 152 swap(heap, pos, child); 153 pos = child; 154 } 155 } 156 157 158 /*** 159 * 前N个最小值 160 */ 161 @Test 162 public void topMinN() { 163 final int len = 200; 164 final int topN = 7; 165 int[] heap = new int[len]; 166 Random random = new Random(); 167 for (int i = 0; i < len; i++) { 168 heap[i] = random.nextInt(10000); 169 } 170 //创建topN最大堆 171 createHeapMax(heap, topN); 172 //遍历数组,并维护topN最小堆 173 findTopMinN(heap,topN); 174 175 printHeap(heap, topN); 176 printArrSorted(heap); 177 System.out.println(); 178 System.out.println(heap[topN-1]); 179 } 180 181 /*** 182 * 遍历数据组,并维护最大堆 183 */ 184 private void findTopMinN(int[] heap, int topN) { 185 for (int i = topN; i < heap.length; i++) { 186 adjustDownTopMinN(heap, topN, i); 187 } 188 } 189 190 /*** 191 * 创建最大堆 192 */ 193 private void createHeapMax(int[] heap, int size) { 194 for (int i = 1; i < size; i++) { 195 adjustUpMax(heap, i); 196 } 197 } 198 199 /*** 200 * 向上调整新加入节点位置 201 */ 202 private void adjustUpMax(int[] heap, int pos) { 203 while (parent(pos) >= 0 && heap[parent(pos)] < heap[pos]) { 204 int parent = parent(pos); 205 swap(heap, parent, pos); 206 pos = parent; 207 } 208 } 209 210 /*** 211 * 向下调整新加入节点位置,并维护最大堆 212 */ 213 private void adjustDownTopMinN(int[] heap, int topN, int pos) { 214 //比topN中最大的还要大直接返回 215 if (heap[0] <= heap[pos]) { 216 return; 217 } 218 swap(heap, 0, pos); 219 pos = 0; 220 while (leftChild(pos) < topN) { 221 int child = leftChild(pos); 222 //判断左右孩子的大小,child代表较大的孩子 223 if (child + 1 < topN && heap[child + 1] > heap[child]) { 224 child++; 225 } 226 //新节点比较大的孩子都大,说明找到位置,直接跳出循环 227 if (heap[child] <= heap[pos]) { 228 break; 229 } 230 swap(heap, pos, child); 231 pos = child; 232 } 233 } 234 }