二叉堆一般用来实现优先队列;优先队列是一种至少允许以下两种操作的数据结构:Insert 以及 DeleteMin;
同查找树一样,二叉堆具有结构性与堆序性,对二叉堆的基本操作可能会破坏这些性质,所以二叉堆的操作要直到其基本性质满足才能结束。
一、结构性
二叉堆在结构上为完全二叉树,其具有完全二叉树的结构性质,如在第k层的结点数,k层的树的最多结点数,k个结点的树的高度等。
二、堆序性
堆序性能使操作快速执行,二叉堆常用来作为优先队列,其需要能快速查找与删除最小值,使用在二叉堆中最小值放在根结点中,由于树的递归定义,子树的根节点也为子树中的最小值,所以在根节点与其子节点中,根节点的值不大于其子节点。
其基本操作:
1、插入
插入元素需要维持堆的两大性质,堆序性的维持需要一些额外的操作;由于插入元素,新建了一个结点在堆的末尾,这里采用一种称为上滤的方法:在堆的末尾新建一个空结点,从新建的空结点出发,若其父亲的值大于插入元素,就使空节点上滤,父亲结点与空节点交换,直到找到某结点的父亲结点的值小于插入值或是找到根节点,由于其没有父亲,查找结束;对于根节点的处理,通常添加一个标记,其为Element[0],设置其值比堆中任意元素小来使while循环结束;该操作复杂度为 O( log N )
2、删除
同插入一样,删除元素也要保持堆的性质不变;保持性质需要将堆中的最后一个结点移动到堆中。这里使用一种称为下滤的技术:将根节点删除后,视为空结点,从根节点开始,将其子节点中较小的上滤,即空结点与较小的子节点交换,若找到某个结点的最小孩子的值大于最后一个结点的值且该节点有两个孩子,则交换;直到结点没有孩子结束寻找,该节点即为最后一个结点插入位置;
实现代码:
struct HeapStruct;
typedef struct HeapStruct *PriorityQueue;
PriorityQueue Initialize( int MaxElements );
void Destory( PriorityQueue H );
void MakeEmpty( PriorityQueue H );
void Insert( ElementType X, PriorityQueue H);
ElementType DeleteMin( PriorityQueue H );
ElementType FindMin( PriorityQueue H );
int IsEmpty( PriorityQueue H );
int IsFull( PriorityQueue H );
struct HeapStruct
{
int Capacity;
int Size;
ElementType *Elements;
};
PriorityQueue Initialize( int MaxElements )
{
PriorityQueue H;
if ( MaxElements < MinPQSize ) // MinPQSize为优先队列最小的容量
printf( "Priority Queue is too small" );
H = (PriorityQueue)malloc( sizeof(HeapStruct) );
if ( H == NULL )
{
printf( "Out of space" );
return;
}
H->Elements = (ElementType *)malloc( sizeof(ElementType) * ( MaxElements + 1 ) );
if ( H->Elements == NULL )
{
printf( "Out of space" );
return;
}
H->Capacity = MaxElements;
H->Size = 0;
H->Elements[0] = MinDate;
return H;
}
void Insert( ElementType X, PriorityQueue H )
{
int i;
if ( IsFull(H) )
{
printf( "Priority queue is full" );
return;
}
for ( i = ++H->Size; H->Elements[i/2] > X; i /= 2 )
H->Elements[i] = H->Elements[i/2];
H->Elements[i] = X;
}
ElementType DeleteMin( PriorityQueue H )
{
int i, Child;
ElementType MinElement, LastElement;
if ( IsEmpty(H) )
{
printf( "Priority queue is empty" );
return H->Elements[0];
}
MinElement = H->Elements[1];
LastElement = H->Elements[H->Size--];
for ( i = 1; i * 2 <= H->Size; i = Child )
{
// Find smaller child
Child = i * 2;
/* 判断左孩子还是右孩子小 */
if ( Child != H->Size && H->Elements[Child + 1] < H->Elements[Child] )
Child++;
/* Percolate one level */
if ( LastElement > H->Elements[Child] )
H->Elements[i] = H->Elements[Child];
else
break;
}
H->Elements[i] = LastElement;
return MinElement;
}
int IsEmpty( PriorityQueue H )
{
return H->Size == 0;
}
int IsFull( PriorityQueue H )
{
return H->Size == H->Capacity;
}
ElementType FindMin( PriorityQueue H )
{
if ( IsEmpty(H) )
{
printf( "Priority queue is empty" );
return H->Elements[0];
}
return H->Elements[1];
}
void MakeEmpty( PriorityQueue H )
{
H->Size = 0;
}
void Destory( PriorityQueue H )
{
if ( H == NULL )
{
printf( "Must create Priority queue first" );
return;
}
else
{
free( H->Elements );
free( H );
}
}