一、堆简介
这里所说的堆是二叉堆,二叉堆是一颗完全二叉树
二叉堆可以分为最大堆和最小堆
最大堆:堆中任意父结点的值总不小于其孩子结点的值
最小堆:堆中任意父结点的值总不大于其孩子结点的值
堆可以用数组来实现
假设数组从下标为0处开始存放元素,则对于任意的一个结点 i, 它的左孩子结点下标为 2i + 1, 它的右孩子结点下标为 2i + 2, 它的父结点的下标为 (i - 1) / 2;
若数组从下标为1处开始存放元素,则对于任意的一个结点i,它的左孩子结点下标为 2i,它的右孩子结点下标为 2i + 1,它的父结点的下标为 i / 2。
这里使用了* 动态数组 *
public class MaxHeap<E extends Comparable <E>> {
private Array<E> array;
public MaxHeap(int capacity){
array = new Array<>(capacity);
}
public MaxHeap(){
array = new Array<>();
}
//将数组转化为堆的形式(Heapify)
public MaxHeap(E[] arr) {
array = new Array<>(arr);
for(int i = parentIndex(arr.length - 1); i >= 0; i-- ) {
siftDown(i);
}
}
//返回堆中元素个数
public int getSize() {
return array.getSize();
}
//判断堆是否为空
public boolean isEmpty() {
return array.isEmpty();
}
/********************辅助方法************************/
//1. 返回索引所对应元素的父结点索引
public int parentIndex(int index) {
if(index > array.getSize() || index < 0)
throw new IndexOutOfBoundsException("Index should between 0 to size");
if(index == 0)
System.out.println("Index of zero has no parent node");
return (index - 1) / 2;
}
//2. 返回索引所对应元素的左孩子索引
public int leftChildIndex(int index) {
if(index > array.getSize() || index < 0)
throw new IndexOutOfBoundsException("Index should between 0 to size");
return 2 * index + 1;
}
// 3. 返回索引所对应元素的左孩子索引
public int rightChildIndex(int index) {
if (index > array.getSize() || index < 0)
throw new IndexOutOfBoundsException("Index should between 0 to size");
return 2 * index + 2;
}
/***************************************************************/
// 向堆中添加元素
/*
* 1. 先将元素添加到数组的尾部,即让它成为最后一个叶子结点
* 2. 让它与其父结点进行比较,若不满足二叉堆的条件,则二者交换位置(上浮操作)
* 3. 对其父结点进行 步骤2 的操作,重复此操作直至父结点为根结点
*/
public void add(E element) {
//添加元素到数组尾部
array.addLast(element);
//上浮操作
siftUp(array.getSize() - 1);
}
// 上浮
private void siftUp(int index) {
while (index > 0 && array.get(index).compareTo(array.get(parentIndex(index))) > 0) {
array.swap(index,parentIndex(index));
index = parentIndex(index);
}
}
//找出最大元素(在数组中为下标为0的元素)
public E findMax() {
if(array.getSize() == 0)
throw new IllegalArgumentException("Can not findMax when heap is empty.");
return array.get(0);
}
//取出堆中的最大元素
/*
* 最大元素即为根结点所对应的元素,取出之后需要重新构建树
* 思路:
* 1. 让根结点和最后一个结点交换位置(当前数组中最后一个元素)
* 2. 删除最后一个结点(此时即为最大结点)
* 2. 对根结点进行下沉操作
*/
public E extractMax() {
E max = findMax();
//交换
array.swap(0, array.getSize() - 1);
//删除
array.removeLast();
//下沉
siftDown(0);
return max;
}
//下沉
/*
* 作用:判断当前结点是否满足堆的定义,
* 不满足时的操作:
* 1. 找出其孩子结点中值较大的那一个
* 2. 父结点和较大孩子结点进行交换
* 3. 递归向下依次进行
*/
private void siftDown(int index) {
//要判断当前结点的孩子结点是否存在(当左孩子不存在时则一定不存在孩子)
while(leftChildIndex(index) < array.getSize()) {
int maxIndex = leftChildIndex(index);
if(maxIndex + 1 < array.getSize() &&
(array.get(maxIndex + 1)).compareTo(array.get(maxIndex)) > 0){
maxIndex++;
}
if(array.get(maxIndex).compareTo(array.get(index)) <= 0) {
break;
}
array.swap(index, maxIndex);
index = maxIndex;
}
}
//替换堆中的最大元素为element
public E repalce(E element) {
E temp = array.get(0);
array.set(0, element);
siftDown(0);
return temp;
}
}