堆作为完全二叉树的一个特例,具有以下特性。
‧ 最底层节点靠左填充,其他层的节点都被填满。
‧ 二叉树的根节点称为“堆顶”,将底层最靠右的节点称为“堆底”。
‧ 对于大顶堆(小顶堆),堆顶元素(根节点)的值是最大(最小)的。
package algorithm;
import java.util.ArrayList;
public class Heap {
private final HeapType mHeapType;
private ArrayList<Integer> mHeaps = new ArrayList<>();
public enum HeapType {
LARGE_ROOT_HEAP,
SMALL_ROOT_HEAP
}
public Heap(HeapType type) {
this.mHeapType = type;
}
/**
* 初始化堆
*
* @param nums 无序数组
*/
public void heapify(int... nums) {
for (int num : nums) {
mHeaps.add(num);
}
int p = parent(mHeaps.size() - 1);
for (int i = p; i >= 0; i--) {
heapifyDown(i);
}
}
/**
* 往堆中插入元素
*
* @param num 元素的值
*/
public void push(int num) {
mHeaps.add(num);
heapifyUp(mHeaps.size() - 1);
}
/**
* 获取堆顶元素
*
* @return 堆顶元素
*/
public int top() {
if (mHeaps.isEmpty()) {
throw new IllegalStateException("can't get top node from an empty Heap!");
}
return mHeaps.get(0);
}
/**
* 弹出堆顶元素
*
* @return 堆顶元素
*/
public int pop() {
if (mHeaps.isEmpty()) {
throw new IllegalStateException("can't pop a node from an empty Heap!");
}
swap(0, mHeaps.size() - 1);
int remove = mHeaps.remove(mHeaps.size() - 1);
heapifyDown(0);
return remove;
}
/**
* 返回堆的大小
*
* @return 堆的大小
*/
public int size() {
return mHeaps.size();
}
private void heapifyUp(int i) {
while (true) {
int p = parent(i);
if (p < 0 || mHeaps.get(p) >= mHeaps.get(i)) {
break;
}
swap(p, i);
i = p;
}
}
private int parent(int i) {
return (i - 1) / 2;
}
private void heapifyDown(int i) {
int size = mHeaps.size();
while (true) {
int curIndex = i;
int leftNodeIndex = left(i);
int rightNodeIndex = right(i);
/**
* if (leftNodeIndex < size && mHeaps.get(leftNodeIndex) > mHeaps.get(i)) {...}
* 这是一个容易忽视的地方,左值比较结束后curIndex已更新,不能通过mHeaps.get(i)来获取maxIndex对应的值
*/
if (mHeapType == HeapType.LARGE_ROOT_HEAP) {
if (leftNodeIndex < size && mHeaps.get(leftNodeIndex) > mHeaps.get(curIndex)) {
curIndex = leftNodeIndex;
}
if (rightNodeIndex < size && mHeaps.get(rightNodeIndex) > mHeaps.get(curIndex)) {
curIndex = rightNodeIndex;
}
} else {
if (leftNodeIndex < size && mHeaps.get(leftNodeIndex) < mHeaps.get(curIndex)) {
curIndex = leftNodeIndex;
}
if (rightNodeIndex < size && mHeaps.get(rightNodeIndex) < mHeaps.get(curIndex)) {
curIndex = rightNodeIndex;
}
}
if (curIndex == i) {
break;
}
swap(i, curIndex);
i = curIndex;
}
}
private void swap(int i, int j) {
int temp = mHeaps.get(i);
mHeaps.set(i, mHeaps.get(j));
mHeaps.set(j, temp);
}
private int right(int i) {
return 2 * i + 2;
}
private int left(int i) {
return 2 * i + 1;
}
public static void main(String[] args) {
Heap heap = new Heap(HeapType.SMALL_ROOT_HEAP);
heap.heapify(6, 3, 9, 21, 32, 24, 16, 78, 92);
int size = heap.size();
for (int i = 0; i < size; i++) {
int pop = heap.pop();
System.out.println(pop);
}
}
}