堆(heap)
什么是堆?
-
优先队列(Priority Queue) : 特殊的“队列”,取出的元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。
-
操作:插入,删除
-
优先队列的完全二叉树表示
-
堆的两个特性
- 结构性:用数组表示的完全二叉树
- 有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
- 最大堆,也称大顶堆:最大值
- 最小堆,也称小顶堆:最小值
-
注意:从根结点到任意结点路径上结点序列的有序性
堆的抽象数据类型描述
-
类型名称:最大堆(MaxHeap)
-
数据对象集:完全二叉树,每个结点的元素值不小于其子结点的元素值
-
操作集:最大堆H∈MaxHeap, 元素item∈ElementType, 主要操作有:
MaxHeap Create( int MaxSize ):创建一个空的最大堆 Boolean IsFull( MaxHeap H ):判断最大堆H是否已满 Insert( MaxHeap H, ElementType item ): 将元素item插入最大堆H Boolean IsEmpty( MaxHeap H): 判断最大堆H是否为空 ElementType DeleteMax( MaxHeap H):返回H中最大元素(高优先级)
最大堆的操作
1. 最大堆的创建
typedef struct HeapStruct *MaxHeap;
struct HeapStruct {
ElementType *Elements; //存储堆元素的数组
int Size; // 堆的目前元素个数
int Capacity; // 堆的最大容量
};
MaxHeap Create( int MaxSize )
{ // 创建容量为MaxSize的空的最大堆
MaxHeap H = malloc( sizeof( struct HeapStruct ));
H->Elements = malloc( (MaxSize+1) * sizeof(ElementType));
H->Size= 0;
H->Capacity = MaxSize;
H->Elements[0] = MaxData;
//定义"哨兵"为大于堆的所有可能元素的值,便于以后更快操作
return H;
}
2. 最大堆的插入
- 算法:将新增结点插入到从其父结点到根结点的有序序列中
void Insert( MaxHeap H, ElementType item )
{
//将元素item插入到最大堆H,其中H->Elements[0]已经定义为从哨兵
int i;
if(IsFull(H)){
printf("最大堆已满");
return;
i = ++H->Size; //i指向插入后堆中的最后一个元素的位置
for( ; H->Elements[i/2] < item; i/=2 )
H->Elements[i] = H->Elements[i/2]; //向下过滤结点
H->Elements[i] = item;//将item插入
}
}
T(N) = O( log N)
最大堆的删除
- 取出根结点(最大值)元素,同时删除堆的一个结点。
ElementType DeleteMax( MaxHeap H )
{
// 从最大堆H中取出键值为最大的元素,并删除一个结点
int Parent, Child;
ElementType MaxItem, temp;
if( IsEmpty(H) ) {
printf("最大堆已为空");
return;
}
MaxItem = H->Elements[1]; //取出根结点最大值
//用最大堆中最后一个元素从根结点开始向上过滤下层结点
temp = H->Elements[H->Size--];
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( temp >= H->Elements[Child] ) break;
else //移动temp元素到下一层
H->Elements[Parent] = H->Elements[Child];
}
H->Elements[Parent] = temp;
return MaxItem;
}
最大堆的建立
-
建立最大堆:将已经存在的N个元素按最大堆的要求存放在一个一维数组中
-
方法一:通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中,其时间代价最大为
O(NlogN)
。 -
方法二:在线性时间复杂度下建立最大堆。
1. 将N个元素按输入顺序存入,先满足完全二叉树的结构特性。
2. 调整各结点位置,以满足最大堆的有序特性。 -
建堆时间复杂性:O(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++; /* Child指向左右子结点的较大者 */
if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
else /* 下滤X */
H->Data[Parent] = H->Data[Child];
}
H->Data[Parent] = X;
}
void BuildHeap( MaxHeap H )
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性 */
/* 这里假设所有H->Size个元素已经存在H->Data[]中 */
int i;
/* 从最后一个结点的父节点开始,到根结点1 */
for( i = H->Size/2; i>0; i-- )
PercDown( H, i );
}