堆
平常所说的堆也就是二叉堆性质比二叉排序树(BST)更简单,堆顶的元素是最值,堆的主要操作有上浮下沉和删除堆顶元素插入元素。
堆的性质:堆顶元素是整个堆中的最大值最小堆堆顶元素是最小值,这个性质在整个二叉堆中每个子节点都适用
堆底层数据结构
堆底层实际存储元素的数据结构是数组,利用数组的索引之间的关系来表示二叉堆的左右子节点为了直观表现我们不使用数组的索引为0的位置
二叉堆的索引有如下关系:
假设堆中某个节点的索引下标为index(在不使用索引为0的位置情况下)
- 父节点下标为 index/2
- 左子节点下标为 index*2
- 右子节点下标为 index*2+1
使用索引为0的位置情况下:
- 父节点下标为(index-1)/2
- 左子节点下标为:index*2+1
- 右子节点下标为:index*2+2
最大堆中主要操作:
- 插入操作:具体做法就是将待插入元素放入数组中最后位置也就是插入二叉堆的叶子节点,然后调用上浮操作让其上浮到合适的位置
- 删除堆顶元素:也就是删除堆中的最值,首先将堆顶元素与数组最后一个元素交换也就是与叶子节点交换然后将其删除再将交换后的堆顶元素下沉到合适的位置
- 上浮:将当前的元素与其父元素比较如果比父元素大则与父元素进行交换,交换之后将再与当前的父元素比较直到找到一个合适的位置
- 下沉:将当前元素与其左右子元素分别比较,如果当前元素比子元素小,找到左右子元素中的最大值与其交换
详细实现如下:
//最大堆
public class MaxHeap<T extends Comparable<T>> {
T []pq; //堆中实际存储元素的容器为数组
int N; //记录堆中元素的个数
public MaxHeap(int cap){
pq = (T[]) new Comparator[cap+1]; //索引为0的 位置不能使用
N = 0; //初始化 才开始堆中没有元素
}
//返回堆顶的最大值
public T getMax(){
return pq[1];
}
//向堆中插入值 向插入 插入最后 然后让其上浮
public void insert(T value){
pq[++N] = value;
swim(N);
}
//删除并且返回堆顶最大的元素
public T delMax(){
T re = pq[1];
exchange(1,N); //交换堆顶和堆底元素 然后让堆顶元素下沉到合适的位置
pq[N--]=null;
sink(1);
return re;
}
//下沉 当前节点的值左/右孩子小 最大堆中根节点都比左右子孩子大
public void sink(int k){
//因为是完全二叉树肯定有左子树但不一定有右子树
while(leftIndex(k)<=N){
int max = leftIndex(k);
if(rightIndex(k)<=N){
max = Math.max(max,rightIndex(k));
}
if(less(k,max)){
exchange(k,max);
k = max;
}else{
break;
}
}
}
//上浮 将节点上浮到合适的位置 最大堆中 根节点比左右孩子都大
public void swim(int k){
while (parentIndex(k)>=1&&less(parentIndex(k),k)){
exchange(parentIndex(k),k);
k = parentIndex(k); //更新k节点的值
}
}
//交换两个节点的值
public void exchange(int m,int n){
T temp = pq[m];
pq[m] = pq[n];
pq[n] = temp;
}
//判断节点的值谁小
public boolean less(int j,int k){
return pq[j].compareTo(pq[k])<0;
}
//返回当前传入节点的左孩子索引
public int leftIndex(int nodeIndex){
return nodeIndex*2;
}
//返回当前传入节点的右孩子索引
public int rightIndex(int nodeIndex){
return nodeIndex*2+1;
}
//返回当前传入节点的根节点的索引
public int parentIndex(int nodeIndex){
return nodeIndex/2;
}
}
最小堆就是将其中的一些判断条件做个改变其余都一样
最大堆/最小堆的应用场景:
1.用堆实现堆排序
2.优先级队列