什么是堆(Heap)
- 堆一定是完全二叉树
- 堆的每一个节点一定不大于或不小于左右孩子
根节点最大的是大根堆,根节点最小的是小根堆
堆的操作
操作集抽象表达(以大根堆为例)
- MaxHeap Create(int MaxSize):创造一个空的大根堆
- Boolean IsFull(MaxHeap H):判断大根堆H是否为满
- Int Insert(MaxHeap H,ElementType item):将元素item插入大根堆H
- Boolean IsEmpty(MaxHeap H):判断大根堆H是否为空
- ElementType DeleteMax(MaxHeap H):删除并返回堆中最大值(根节点)
具体实现
大根堆的结构
typedef struct HeapStruct *MaxHeap;
struct HeapStruct{
ElementType *Elements; //堆元素,数组形式存储
int Size; //元素个数
int Capacity //堆容量
}
创建大根堆
MaxHeap Create(int MaxSize)
{
MaxHeap H = (MaxHeap)malloc(sizeof(struct HeapStruct));
H->Elements = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType));
H->Size = 0;
H->Capacity = MaxSize;
H->Elements[0] = MaxData; //哨兵,大根堆存入最大值,小根堆最小值
return H;
}
插入堆元素
- 判断堆是否已满
- 把新元素插入堆底最后一个元素后面
- 与父节点比较,大于就上移,小于说明符合大根堆
int Insert(MaxHeap H,ElementType item)
{
int i;
if(IsFull(H)){
printf("堆已满");
return 0;
}
i=H->Size++; //i指向对最后一个元素位置,同时堆元素个数+1
for(;H->Elements[i/2]<item;i/=2){ //由于哨兵存在不需要判断i
H->Elements[i]=H->Elements[i/2]; //插入元素上移
}
H->Elements[i]=item;
return 1;
}
删除堆元素(出堆)
- 将堆顶元素取出,将堆底最后一个元素移到堆顶
- 如果有左右孩子,左右孩子比较大小
- 左右孩子最大的与父元素比较,大于说明符合大根堆
- 小于则与左右孩子最大值交换,并继续与左右孩子比较,
- 直到符合大根堆或者叶节点
ElementType DeleteMax(MaxHeap H)
{
int parent,child;
ElementType MaxItem,temp;
if(IsEmpty(H)){
printf("堆已空");
return 0;
}
MaxItem = H->Elements[1];//取堆元素最大值
temp = H->Elements[H->Size--];//取堆最后一个元素作为堆顶元素,堆元素个数-1
for(parent=1;parent*2<=H->Size;parent=child){
child=parent*2;
if(child!=H->Size&&(H->Elements[child]<H->Elements[child+1])){
child++;
} //令child指向左右孩子最大值
if(H->Elements[child]<=temp){
break; //堆顶当前元素就是最大的
}else{
H->Elements[parent] = H->Elements[child]; //堆顶元素下移
}
}
H->Elements[parent]=temp;
return MaxItem;
}
杂记
建堆的初始排序有两种
- 边插入边排序,时间复杂度为O(nlogn)
- 先全部插入,再进行排序,时间复杂度为O(n)
注意:两者实现后初始堆可能出现不同,表现为堆底元素可能左右互换