一、堆结构
1)堆结构就是用数组实现的完全二叉树结构
2)完全二叉树中如果每棵子树的最大值都在顶部就是大根堆
3)完全二叉树中如果每棵子树的最小值都在顶部就是小根堆
4)堆结构的heapInsert与heapify操作
5)堆结构的增大add和减少poll
6)优先级队列结构,就是堆结构
heapInsert:
入堆排序操作,从数组最后一个位置插入,然后再与其父节点(i-1)/2比较大小,大则交换上去,接着往其爷节点持续走..直到顶,或小于当前节点的父节点则停止,完成排序
heapify:
堆下沉排序操作 剔除元素后,需要将交换到根部的元素往下沉判断排序,如果大于左右节点就不用动,小于则与左右较大节点交换,并下沉继续判断,直到底部或者大于左右子节点
代码演示:
package class06;
import java.util.Comparator;
import java.util.PriorityQueue;
public class Heap {
//大根堆 每个子树根节点比左右节点大
public static class MyMaxHeap {
private int[] heap;
private int heapSize;
private final int limit;
public MyMaxHeap(int limit) {
heap = new int[limit];
heapSize = 0;
this.limit = limit;
}
public boolean isEmpty() {
return heapSize == 0;
}
public boolean isFull() {
return limit == heapSize;
}
//入堆,同时保持堆的大根堆 有序
public void push(int value) {
if (isFull()) {
throw new RuntimeException("堆已满,无法添加元素!");
}
//没满就赋值追加到数组后
heap[heapSize] = value;
//依次与该元素的父节点比较大小,大则交换两元素,然后接着往上走,直到顶或者不大于父节点,同时最后要把size+1
heapInsert(heap, heapSize++);
}
//入堆操作,从数组最后一个位置插入,然后再与其父节点(i-1)/2比较大小,大则交换上去,接着往其爷节点持续走..直到顶,或小于当前节点的父节点则停止,完成排序
private void heapInsert(int[] heap, int i) {
//这个条件判断了两种情况,一个是大于父节点,一个是还没到顶节点(假如i来到顶部0 那么(i-1)/2也等0 为自己,是等于) 则继续循环。
while (heap[i] > heap[(i - 1) / 2]) {
swap(heap, i, (i - 1) / 2);
i = (i - 1) / 2;
}
}
//出堆,弹出最大值,顶部,然后保证当前堆仍有序 是大根堆
public int pop() {
if (isEmpty()) {
throw new RuntimeException("堆已空,无法弹出元素!");
}
//弹出首元素,最大值
int ans = heap[0];
//然后把元素剔除两步 1.将首元素,与尾元素(heapSize是长度,尾元素是heapSize-1)交换,因为弹出操作,需要将heapSize 元素个数-1,两个操作只需要用--heapSize就能符合
swap(heap, 0, --heapSize);
//2.交换后表示将根节点元素剔除,然后需要确保现有堆的顺序
heapify(heap, 0, heapSize);
return ans;
}
//堆下沉排序操作 剔除元素后,需要将交换到根部的元素往下沉判断排序,如果大于左右节点就不用动,小于则与左右较大节点交换,并下沉继续判断,直到底部或者大于左右子节点
private void heapify(int[] heap, int i, int heapSize) {
//首先判断是否存在左子节点,左节点索引是i*2+1,不能超过heapSize-1尾索引,如果超过那肯定就到最后一个元素,右节点是比左节点大1 也更不会存在
while (i * 2 + 1 < heapSize) {
//此时确定有左节点,但需要判断是否有右节点i*2+2 如果有 并且大于左节点,那么左右节点较大值就是右节点,否则就是左节点
int largest = i*2+2 < heapSize && heap[i*2+2]>heap[i*2+1]?i*2+2:i*2+1;
//然后把较大的节点与父节点比较,谁大则重新赋值 largest最大值
largest = heap[largest] > heap[i]?largest:i;
if(largest == i) break; //如果判断后这个最大值位置就是父节点位置,相当于父节点都大于子节点,那么就不用交换,再下沉,此时已经完成排序,大的仍旧在前面,小的在下面,直接退出循环
//子节点大于当前节点,那么与其较大的节点交换,交换完之后,当前节点i要来到较大节点largest位置 循环下沉
swap(heap,i,largest);
i = largest;
}
}
private void swap(int[] heap, int i, int j) {
int temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
}
public static class RightMaxHeap {
private int[] arr;
private final int limit;
private int size;
public RightMaxHeap(int limit) {
arr = new int[limit];
this.limit = limit;
size = 0;
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == limit;
}
public void push(int value) {
if (size == limit) {
throw new RuntimeException("heap is full");
}
arr[size++] = value;
}
public int pop() {
int maxIndex = 0;
for (int i = 1; i < size; i++) {
if (arr[i] > arr[maxIndex]) {
maxIndex = i;
}
}
int ans = arr[m