标签:path 个数 否则 结构性 相同 blog 名称 个性 通过
设计一种堆结构像二叉堆那样高效的支持合并操作而且只使用一个数组似乎很困难。原因在于,合并似乎需要把一个数组拷贝到另一个数组中去,对于相同大小的堆,这将花费O(N)。正因为如此,所有支持高效合并的高级数据结构都需要使用指针。
像二叉堆那样,左式堆也有结构性和堆序性。不仅如此,左式堆也是二叉树,它和二叉堆之间的唯一区别在于:左式堆不是理想平衡的,而实际上是趋向于非常不平衡。
把任意节点X的零路径长(null path length, NPL)Npl(X)定义为从X到一个没有两个儿子的节点的最短路径长。因此,具有0个或1个儿子的节点的Npl值为0,而Npl(NULL)=-1。注意,任意节点的零路径长比它的各个儿子节点的最小值多1。
左式堆的性质是:对于堆中的每一个节点X,左儿子的零路径长至少与右儿子的零路径长一样大。因此,下图1中,左边的二叉树是左式堆,而右边的二叉树则不是。这个性质使左式堆明显更偏重于使树向左增加深度,左式堆的名称也由此而来。
图1:两棵树的零路径长,只有左边的树是左式堆
左式堆的基本操作是合并(Merge),注意,插入只是合并的特殊情形。因为可以把插入看成是单节点堆与一个大的堆的Merge。对于合并操作,主要采取的是递归解法。如图2所示,两个左式堆H1,H2,注意,最小元在根处。除数据,左指针,右指针外,每个单元还需要有一个指示零路径长的项。
图2:两个左式堆H1,H2
如果这两个堆有一个是空的,那么可以直接返回。否则,为了合并两个堆,需要比较它们的根。首先,将具有大的根值的堆与具有较小的根值的堆的右子树堆合并。在本例中,我们递归的将H2与H1在8处的右子树堆合并,得到如图3中的堆。
图3:将H2与H1的右子树堆合并的结果
如果直接将图3中的堆作为H1的右儿子,如图4所示,那么新的H1虽然满足堆序性质,但是它不是左式堆,因为左子树的零路径长为1,而根的右儿子的零路径长为2.因此,左式堆的性质在根处被破坏。不过,很容易看到,树的其余部分必然是左式堆,由于递归步骤,根的右子树也是左式堆。根的左子树没发生变化,必然也是左式堆。这样一来,只需要对根进行调整就可以了。使整个树是左式堆的做法如下:只要交换根的左儿子和右儿子,并更新零路径长,就完成了Merge。如图5所示:
图4:H1接上图3中的左式堆作为右儿子的结果
图5:交换H1的根的儿子得到的结果
标签:path 个数 否则 结构性 相同 blog 名称 个性 通过
原文地址:http://www.cnblogs.com/anzhi/p/7536435.html