数据结构-堆
基本概念
堆是一种用数组来实现的完全二叉树。堆分为两种:最大堆(大顶堆)和最小堆(小顶堆),差别在于结点的排序方式。在最大堆中,任一结点的关键字是其子树所有结点的最大值,而在最小堆中,任一结点的关键字是其子树所有节点的最小值。
在此我们暂时只讨论最大堆,也叫做优先队列。
抽象数据类型描述
类型名称:最大堆(MaxHeap)
数据对象集:完全二叉树,每个结点的元素值不小于其子结点的元素值
操作集:最大堆H是MaxHeap类型,元素item是ElementType类型
- MaxHeap Create(int MaxSize):创建一个空的最大堆
- bool IsFull(MaxHeap H):判断最大堆H是否已满
- void Insert(MaxHeap H, ElementType item):将元素item插入最大堆H
- bool IsEmpty(MaxHeap H):判断最大堆H是否为空
- ElementType DeleteMax(MaxHeap H):返回H中最大元素(高优先级)
结构体定义
typedef struct HNode *Heap;
struct HNode{
ElementType *Data; //存储元素的数组
int Size; //堆中当前元素个数
int Capacity; //堆的最大容量
};
typedef Heap MaxHeap; //最大堆
typedef Heap MinHeap; //最小堆
最大堆的创建
MaxHeap Create(int MaxSize){
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; //定义哨兵为大于堆中所有可能元素的值 #define MAXDATA 1000
return H;
}
判断是否为满
bool IsFull(MaxHeap H){
return (H->Size==H->Capacity);
}
最大堆的插入
把新元素插入最大堆时,先把新元素放在最大堆的末尾,再与父结点比较大小,如果比父结点大,则交换位置,直到小于父结点,便确定好了插入位置。这一过程叫做上滤。
void Insert(MaxHeap H, ElementType X){
int i;
if(IsFull(H)){
printf("最大堆已满");
return;
}
i=++H->Size; //先把新元素放在最大堆的末尾
for(;H->Data[i/2]<X;i/=2) //依次与父结点比较
H->Data[i]=H->Data[i/2]; //上滤X
H->Data[i]=X;
}
判断是否为空
bool IsEmpty(MaxHeap H){
return (H->Size==0);
}
最大元素的返回
因为最大堆的最大元素是根结点,如果直接返回根结点的话,整个堆就被删除了,所以我们把最大元素和最大堆的末尾元素交换,返回并删除末尾元素,再对最大堆进行调整。调整的过程叫做下滤(PercDown),具体来说就是从根结点开始,与两个子结点的最大值进行比较,小于便与之交换,知道大于子结点的最大值。
ElementType DeleteMax(Heap H){
int Parent,Child;
ElementType MaxItem,X;
if(IsEmpty(H)){
printf("最大堆已空");
return ERROR; //#define ERROR -1
}
MaxItem=H->Data[1]; //取出根结点存放的最大元素
X=H->Data[H->Size--]; //最大堆的规模-1
for(Parent=1;Parent*2<=H->Size;Parent=Child){
Child=Parent*2;
if((Child!=H->Size)&&(H->Data[Child]<H->Data[Child+1]))
Child++; //Child指向左右子结点中的最大值
if(X>=H->Data[Child])
break; //大于子结点,则找到了位置
else
H->Data[Parent]=H->Data[Child]; //下滤X
}
H->Data[Parent]=X;
return MaxItem;
}
最大堆的建立
建立最大堆:将已经存在的N个元素按最大堆的要求存放在一个一维数组中。有两种方法
- 通过插入操作,将N个元素一个个相继插入到一个初始为空的最大堆中,其时间复杂度为O(NlogN)。
- 在线性复杂度下建立最大堆,将N个元素按输入顺序存入,先满足完全二叉树的结构特性,再调整各结点位置,以满足最大堆的有序特性。具体过程则是从最后一个父结点开始,到根结点,依次进行下滤操作。
我们讨论第2种方法。
void PercDown(MaxHeap H, int p);
void BuildHeap(MaxHeap H){
int i;
for(i=H->Size/2;i>0;i--) //从最后一个父结点k开始,到根结点1
PercDown(H, i);
}
void PercDown(MaxHeap H, int 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++; //Child指向左右子结点的最大值
if(X>=H->Data[Child])
break; //大于子结点,则找到了位置
else
H->Data[Parent]=H->Data[Child]; //下滤X
}
H->Data[Parent]=X; //X放入合适位置
}