配对堆的定义
一个配对堆是一棵满足堆序性质的树,树上每个节点保存其关键字、子节点指针、兄弟节点指针和前驱指针。当该节点为其父节点的第一个儿子,即父节点的子节点指针指向该节点时,该节点的前驱指针指向其父节点;否则该节点的前驱指针指向该节点的前一个兄弟,即该节点的前驱指针指向的节点的兄弟节点指针指向该节点。
配对堆可以以较高的效率支持合并维护、单元素插入维护、单元素关键字减值维护、单最值查询删除维护。从经验上看,配对堆的时间复杂度和斐波那契堆的类似,不过关于此点截止到笔者撰写本文为止尚未得到理论证明。
对于配对堆的定义代码如下:
#define NIL -1
struct PAIRING_HEAP_NODE
{
int child,key,prev,sibling;
}PHData[MAX_N];
struct PAIRING_HEAP
{
int root;
}PairHeap[MAX_N];
配对堆的合并维护
将根节点关键字较大的配对堆的根节点,作为根节点关键字较小的配对堆的根节点的第一个儿子,更新指针,即可在常数时间内完成合并维护。
对于配对堆的合并维护代码如下:
PAIRING_HEAP PH_Merge(PAIRING_HEAP ph1,PAIRING_HEAP ph2)
{
PAIRING_HEAP ph0;
if(ph1.root!=NIL)
{
ph0=ph1;
if(ph2.root!=NIL)
{
if(PHData[ph0.root].key<PHData[ph2.root].key)
if(PHData[ph0.root].child!=NIL)
PHData[PHData[ph0.root].child].sibling=ph2.root,
PHData[ph2.root].prev=PHData[ph0.root].child;
else
PHData[ph0.root].child=ph2.root,
PHData[ph2.root].prev=ph0.root;
else
PHData[ph0.root].sibling=PHData[ph2.root].child,
PHData[PHData[ph2.root].child].prev=ph0.root,
PHData[ph2.root].child=ph0.root,
PHData[ph0.root].prev=ph2.root,
ph0=ph2;
}
}
else
ph0=ph2;
return ph0;
}
配对堆的单元素插入维护
在单元素插入维护中,将单元素看作一个只有一个元素的配对堆,和待插入的配对堆合并即可。
对于配对堆的单元素插入维护代码如下:
int PH_Memory[MAX_N],PH_MemTop=0;
int PH_NewNode()
{
if(PH_Memory[0]>0)
return PH_Memory[PH_Memory[0]--];
return PH_MemTop++;
}
void PH_Insert(PAIRING_HEAP &ph0,int key)
{
PAIRING_HEAP pht;
pht.root=PH_NewNode();
PHData[pht.root].child=PHData[pht.root].prev=PHData[pht.root].sibling=NIL;
PHData[pht.root].key=key;
ph0=PH_Merge(pht,ph0);
}
配对堆的单元素关键字减值维护
由于无法确定单元素关键字减值后是否破坏堆序,故将以该节点为根的整个子树从其父节点的子节点的兄弟指针链里删去,作为一个新配对堆,与原配对堆合并即可。
对于配对堆的单元素关键字减值维护代码如下:
void PH_DecreaseKey(PAIRING_HEAP &ph0,int pos,int k)
{
PAIRING_HEAP pht;
if(PHData[pos].key<k)
return;
PHData[pos].key=k;
if(PHData[pos].prev!=NIL)
{
if(PHData[pos].sibling!=NIL)
PHData[PHData[pos].sibling].prev=PHData[pos].prev;
if(PHData[PHData[pos].prev].child==pos)
PHData[PHData[pos].prev].child=PHData[pos].sibling;
else
PHData[PHData[pos].prev].sibling=PHData[pos].sibling;
PHData[pos].prev=PHData[pos].sibling=NIL;
pht.root=pos;
ph0=PH_Merge(pht,ph0);
}
}
配对堆的单最值查询删除维护
根据堆序,配对堆中的最小元素即该配对堆根节点的关键字。将根节点删除后,我们先将所有子树两两合并,这样子树数目减半后,从最后一个子树向前合并,得到维护后的新配对堆。
对于配对堆的单最值查询删除维护代码如下:
#define PH_FindMin(ph0) (ph0.root!=NIL?PHData[ph0.root].key:NIL)
PAIRING_HEAP PH_DeleteNode(int pos)
{
PAIRING_HEAP ph0;
ph0.root=PHData[pos].child;
PHData[ph0.root].prev=PHData[pos].child=PHData[pos].key=PHData[pos].prev=PHData[pos].sibling=NIL;
PH_Memory[++PH_Memory[0]]=pos;
return ph0;
}
PAIRING_HEAP PH_MergePair(PAIRING_HEAP ph0)
{
if(PHData[ph0.root].sibling==NIL)
return ph0;
PAIRING_HEAP ph1;
ph1.root=PHData[ph0.root].sibling;
PHData[ph0.root].sibling=PHData[ph1.root].prev=NIL;
if(PHData[ph1.root].sibling==NIL)
return PH_Merge(ph0,ph1);
PAIRING_HEAP ph2;
ph2.root=PHData[ph1.root].sibling;
PHData[ph1.root].sibling=PHData[ph2.root].prev=NIL;
return PH_Merge(PH_Merge(ph0,ph1),
PH_MergePair(ph2));
}
int PH_DeleteMin(PAIRING_HEAP &ph0)
{
int minum=PH_FindMin(ph0);
if(ph0.root!=NIL)
ph0=PH_MergePair(PH_DeleteNode(ph0.root));
return minum;
}
配对堆的总结
作为一种易于实现的数据结构,配对堆可谓是斐波那契堆的简化版。不过,也正是由于配对堆的不规则性,故其理论时间复杂度的分析较为困难。但在实践中,配对堆不失为一种高效简便的数据结构。