定义
底层基于数组,节点计算通过公式
//因为heap是一棵完全二叉树,所以很方便可以基于数组实现
public class MaxHeap<E extends Comparable<E>> {
private List<E> data;
public MaxHeap(int capacity) {
data = new ArrayList<>(capacity);
}
public MaxHeap() {
data = new ArrayList<>();
}
辅助函数,利用数组索引实现树结构
//辅助函数得到父子索引
private int parent(int index) {
if (index == 0)
throw new IllegalArgumentException("dont have parent");
return (index - 1) / 2;
}
private int rightChild(int index) {
return index * 2 + 1;
}
private int leftChild(int index) {
return index * 2 + 2;
}
向堆添加元素
基于的是java自带的数组,交换操作是借助Collections工具类的swap方法
核心操作为signUp上浮(复杂度为logn)
操作步骤
1.将元素加到数组尾。
2.循环进行上浮,即如果比父节点大,则和父节点交换位置
3.循环终止条件,k=0 到达顶部或者符合最大堆性质
public void add(E e) {
data.add(e);//向数组最后添加元素,arraylist本身add就是添加到最后
siftUp(data.size() - 1);//传入想上浮元素的索引
}
//上浮 传入要上浮的元素的索引
//conllections工具的swap方法
private void siftUp(int k) {
while (k>0&&data.get(parent(k)).compareTo(data.get(k)) < 0) {//k比k的父亲节点大
Collections.swap(data, k, parent(k));
k = parent(k);//下一轮再看这个父亲节点的是否需要上浮,是否满足堆的性质
//k等于0到达树顶或者满足性质停止上浮
}
}
从堆中取出元素
核心操作 下沉signDown(复杂度logn)
操作步骤
1.找出左右子节点中的最大值索引j。
2.如果父节点比j☞小 ,与j交换。
3.终止条件 leftChild(k)>data.size();左孩子越界即没有子节点。
//取出堆中最大元素
public E extraMax() {
E ret = findMax();
Collections.swap(data, 0, size() - 1);//交换最大和最小元素
data.remove(size() - 1);//取出堆尾,即最大元素
siftDown(0);//将堆顶最小元素下沉,保证堆的性质
return ret;
}
private void siftDown(int k) {
while (leftChild(k) < data.size()) {//左孩子没有越界有左孩子节点
int j = leftChild(k);
if (rightChild(k) < data.size()
&& data.get(rightChild(k)).compareTo(data.get(j)) > 0) {
j = rightChild(k);
//此时data[j]为左右孩子中大的那个
}
if (data.get(k).compareTo(data.get(j)) > 0)//已经满足性质
break;
Collections.swap(data, k, j);
k = j;//进行下一轮循环,判断
}
}
replace()
//replace 取出最大元素 再加入一个元素
public E replce(E e) {//堆顶替换为e 再把索引0下沉
E ret = data.get(0);
data.set(0, e);
siftDown(0);
return ret;
}
replace第一想法是删除元素,在添加元素。但这样树妖进行两次logn操作(上浮,下沉都是longn)。
所以步骤是堆顶替换e 再把堆顶下沉。只进行一次logn操作。
heapify :将数组整理成最大堆的顺序