堆:逻辑上是一颗完全二叉树
物理上是数组的形式 ,顺序存储
作用:找数组中的前K大的值
特点:
大顶堆:任意一个root的值>=左右子树的值
小顶堆:任意一个root的值<=左右子树的值
–要调整root所在的节点
前提:root的左右子树都已满足堆的性质
如果root所在的节点已经是叶子结点,调整结束
找到左右孩子中较小的一个min
堆的基本操作(以大顶堆为例)
查找堆元素的父节点,左右子树节点:
``
//数组下标为0的元素也存储数据的情况
//返回完全二叉树的数组表示中,
// 一个索引表示的元素的父亲节点的索引
private int parent(int index) {
if(index == 0)
throw new IllegalArgumentException("0没有父亲节点");
return (index-1)/2;
}
//返回完全二叉树的数组表示中,
// 一个索引表示的元素的左孩子节点的索引
private int leftChild(int index) {
return index*2 + 1;
}
//返回完全二叉树的数组表示中,
// 一个索引表示的元素的右孩子节点的索引
private int rightChild(int index) {
return index*2 + 2;
}
添加元素:
``
//向堆中添加元素
public void add(E e) {
data.add(e);
siftUp(data.size()-1);
}
private void siftUp(int k) {
while(k > 0 &&
//data的父亲节点如果小于data,就交换data与父亲节点
data.get(parent(k)).compareTo(data.get(k)) < 0) {
swap(k,parent(k));
k = parent(k);
}
}
//交换元素:使用Collections工具类的swap方法
``
private void swap(int i,int j) {
if(i < 0 || i >= data.size() || j < 0 || j > data.size())
throw new IllegalArgumentException("非法下标");
Collections.swap(data,i,j);
}
取出堆中最大元素:
``
//查看堆中最大元素(堆顶)
public E peek() {
if(data.size() == 0)
throw new IllegalArgumentException("当前为空堆");
return data.get(0);
}
//取出堆中最大元素(删除)
public E extractMax() {
E ret = peek();
swap(0,data.size() -1);
data.remove(data.size()-1);
siftDown(0);
return ret;
}
private void siftDown(int k) {
while(leftChild(k) < data.size()) {
//比较k节点的左右节点中较大的一个
int j = leftChild(k);
if(j+1 < data.size() && data.get(j+1).compareTo(data.get(j)) > 0) {
j = rightChild(k);
//data[j]时左右子节点中较大值
}
if(data.get(k).compareTo(data.get(j)) >= 0) {
break;
}
swap(k,j);
k = j;
}
}
replace方法,替换堆顶元素
``
//取出堆中最大元素,并替换为元素e
public E replace(E e) {
E ret = peek();
data.set(0,e);
siftDown(0);
return ret;
}
heapify方法,将传入的数组改造为堆
``
//将任意数组整理成堆的形状
public void heapify(Integer[] arr){
if(arr == null)
throw new IllegalArgumentException("非法数组");
for(int i = parent(arr.length - 1) ;i >= 0;i--) {
siftDown(i);
}
}