堆:
1.任意节点小于(或大于)它的所有后裔,最小元(或最大元)在堆的根上(堆序性)。
2.堆总是一棵完全树。
完全树:即除了最底层,其他层的节点都被元素填满,且最底层尽可能地从左到右填入。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。
支持的基本操作:
操作 | 描述 | 时间复杂度 |
---|---|---|
build | 创建一个空堆 | O(n) |
insert | 向堆中插入一个新元素 | O(logn) |
update | 将新元素提升使其匹配堆的性质 | |
get | 获取当前堆顶元素的值 | O(1) |
delete | 删除堆顶元素 | O(logn) |
heapify | 使删除堆顶元素的堆再次成为堆 |
二叉堆的数组实现:给每个节点编号,使得每个父节点的左节点序列号为父节点的二倍,右节点的序列号为父节点序列号的二倍加1
parent(i) = i/2
left child(i) = 2*i
right child(i) = 2*i + 1
堆的基础实现:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<ctime>
#include<cmath>
#include<cmath>
using namespace std;
template<typename Item>
class MaxHeap{
private:
Item* data;
int count;
public:
MaxHeap(int capacity){
data=new Item[capacity+1]; //从索引1开始存储
count=0;
}
~MaxHeap(){
delete [] data;
}
int size(){
return count;
}
bool isEmpty(){
return count==0;
}
};
int main(){
MaxHeap<int> maxHeap = MaxHeap<int>(100);
cout<<maxHeap.size()<<endl;
return 0;
}
堆的基本操作:
1.ShiftUp:先将元素写入数组最后一个位置,然后不断和父节点比较大小,如果该元素大于父节点,则进行交换,直到找到合适的位置。
实现:
void ShiftUp(int k){
while(k>1&&data[k]>data[k/2]){
swap(data[k],data[k/2]);
k/=2;
}
}
插入一个数组元素:
void insert(Item item){
data[count+1]=item;
count++;
ShiftUp(count);
}
2.ShiftDown:从堆中取出一个元素(优先级最高的元素),然后把最后一个元素移动到第一个元素的位置。不断与两个子节点中较大的进行交换,直到找到合适的位置。
实现:
void shiftDown(int k){
//k必须有孩子,只要满足有左孩子即可
while(2*k<=count){
int j=2*k; //在此轮循环中,data[k]和data[j]交换位置
if(j+1<=count&&data[j+1]>=data[j])
j+=1;
if(data[k]>=data[j])
break;
swap(data[k],data[j]);
k=j;
}
}
取出一个元素:
Item extractMax(){
assert(count>0);
Item ret=data[1];
swap(data[1],data[count]);
count--;
shiftDown(1);
return ret;
}