最大堆的最常用表示是一棵二叉树,不特指的话,它是一棵完全二叉树。
最大堆的特性
1、结构特性: 用数组表示完全二叉树
2、部分有序性: 任一结点的值大于等于其子结点的值(兄弟节点之间没有约束)
(基本概念什么的就不多说了,直接上代码)
最大堆的操作集
MaxHeap CreatHeap (int MaxSize) //最大堆的创建,最大长度为MaxSize ;
bool IsFull (MaxHeap H) //判断堆是否已满 ;
bool Insert (MaxHeap H, ElementType X) //插入元素;
bool IsEmpty (MaxHeap H) //判断堆是否为空 ;
ElementType DeleteMax (MaxHeap H) //删除并返回堆顶元素;
最大堆的结构如下
typedef struct HNode* Heap; //堆的类型定义
typedef int ElementType ;
struct HNode{
ElementType *Data; //储存元素的数组
int Size; //堆中当前元素个数
int Capacity; //堆的最大容量
};
typedef Heap MaxHeap; //最大堆
建立最大堆
#define MAXDATA 1000
MaxHeap CreatHeap (int MaxSize){
//从数组下标为 1的位置储存数据
MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
H->Data = (ElementType*)malloc((MaxSize+1)*sizeof(ElementType));
H->Size = 0;
H->Capacity = MaxSize;
H->Data[0] = MAXDATA; //定义 “哨兵 ”,需大于堆中其他所有值;
/* 哨兵可以让堆的插入代码更简洁优美 */
return H;
}
最大堆的插入操作
bool IsFull (MaxHeap H){
return (H->Size==H->Capacity); //当前元素个数达到最大容量,说明已满
}
bool Insert (MaxHeap H, ElementType X){
int i;
if(IsFull(H)){
printf("最大堆已满");
return false;
}
i = ++H->Size; //让i指向堆中最后一个元素位置
for( ;H->Data[i/2]<X; i/=2)
H->Data[i] = H->Data[i/2] ; //上滤 x
H->Data[i] = X;
/*上滤即比较x与最后一个结点i的父结点,若小于父结点则把x 放在 i的位置,若大于父结点,
把原父结点的值放在结点i的位置,如此不断顺着父结点到根节点的路径向上比较 */
return true;
}
最大堆的删除操作
bool IsEmpty (MaxHeap H){
return (H->Size==0);
}
#define ERROR -1 //标识错误
// 删除操作要比插入复杂些
//是用最大堆中最后一个元素从根节点开始向上过滤下层结点
ElementType DeleteMax (MaxHeap H){
int Parent, Child;
ElementType MaxItem, X;
if(IsEmpty(H)){
printf("最大堆已空");
return ERROR;
}
MaxItem = H->Data[1]; //取出根节点存放的最大值
X = H->Data[H->Size--]; //减小堆的规模,x是堆中最后一个元素
for(Parent=1; Parent*2<=H->Size; Parent=Child){ //Parent*2<=H->Size 判断该结点有没有子结点
Child = Parent*2; //Parent 为父结点,Child为子结点
if((Child!=H->Size)&&(H->Data[Child]<H->Data[Child+1]))
Child++; //Child指向子结点中的较大者
if(X>=H->Data[Child])
break; //若x大于结点Parent的两个子结点,则跳出把x放在Parent的位置
/* 否则把子结点中较大的元素上移到父结点的位置,再以该子结点作为父结点下滤 x*/
else
H->Data[Parent] = H->Data[Child];
}
H->Data[Parent] = X;
return MaxItem; //返回堆顶元素
}
建立最大堆
此处的建立是指如何让将数组中已存在的N个元素按照最大堆的要求存放,(可以调用前面的插入函数,但用以下算法时间代价更小)
void PercDown (MaxHeap H, int p){
/* 下滤,将H中以 H->Data[p] 为根的子堆调整为最大堆 */
int Parent,Child;
ElementType X;
X = H->Data[p] ;
for(Parent=p;Parent*2<=H->Size;Parent=Child){
Child=Parent*2;
if((Child!=H->Size)&&(H->Data[Child]<H->Data[Child+1]))
Child++;
if(H->Data[Child]<=X)
break;
else
H->Data[Parent]=H->Data[Child];
}
H->Data[Parent]=X;
}
void BuildHeap (MaxHeap H){
//调整 H->Data[]中的元素使满足最大堆的有序性
//这里假设所有的 H->Size 个元素已经在 H->Data[]中
int i;
//从最后一个元素的父结点开始,直至根节点
for(i=H->Size/2;i>0;i--)
PercDown(H, i);
}
下面给出一个用法这种方法建立最大堆的例子
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct HNode* Heap; //堆的类型定义
typedef int ElementType ;
struct HNode{
ElementType *Data; //储存元素的数组
int Size; //堆中当前元素个数
int Capacity; //堆的最大容量
};
typedef Heap MaxHeap; //最大堆
#define MAXDATA 1000
MaxHeap CreatHeap (int MaxSize){
/* 在这里稍微改了下CreatHeap()函数 */
MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
H->Data = (ElementType*)malloc((MaxSize+1)*sizeof(ElementType));
H->Size = 0;
H->Capacity = MaxSize;
H->Data[0] = MAXDATA; //定义 “哨兵 ”,需大于堆中其他所有值;
for(H->Size=1;H->Size<=MaxSize;H->Size++){
scanf("%d",&(H->Data[H->Size]));
}
H->Size--;
/*由于H->Size表示当前元素个数,且正好是数组最后一个值的下标,
但for循环结束后H->Size的值为数组最后一个值的下标+1,所以要做 -1 处理*/
return H;
}
void PercDown (MaxHeap H, int p){
/* 下滤,将H中以 H->Data[p] 为根的子堆调整为最大堆 */
int Parent,Child;
ElementType X;
X = H->Data[p] ;
for(Parent=p;Parent*2<=H->Size;Parent=Child){
Child=Parent*2;
if((Child!=H->Size)&&(H->Data[Child]<H->Data[Child+1]))
Child++;
if(H->Data[Child]<=X)
break;
else
H->Data[Parent]=H->Data[Child];
}
H->Data[Parent]=X;
}
void BuildHeap (MaxHeap H){
//调整 H->Data[]中的元素使满足最大堆的有序性
//这里假设所有的 H->Size 个元素已经在 H->Data[]中
int i;
//从最后一个元素的父结点开始,直至根节点
for(i=H->Size/2;i>0;i--)
PercDown(H, i);
}
int main(void){
int k;
MaxHeap H=CreatHeap ( 10);
for(k=0; k<H->Size; k++)
printf("%d ",H->Data[k+1]);
printf("\n");
BuildHeap(H);
for(k=0; k<H->Size; k++)
printf("%d ",H->Data[k+1]); //下标为0处存的是哨兵,所以1~Size才是有用的数据
printf("\n");
free(H);
return 0;
}
最小堆的相关操作和最大堆操作的方法一致,只需微小改动,可以自己尝试一下。最大堆的笔记就先整理这么多了,若有错误之处,希望大家不吝指正。