那它是怎么做到这么快的呢?现在讨论这个还为时过早,因为首先要回答另一个问题。前面第三自然段提到过:它不是理想平衡的,而是趋向于极其不平衡。不平衡的话结构性就荡然无存了,但我们需要明白的是:对于Heap,堆序才是本质特征,其他的都无所谓,在必要时刻都可以牺牲掉,毕竟,计算机科学就是一门关于权衡的学问。
现在我们来讨论一下左式堆的性质,引入一个概念:零路径长(null path length,npl)定义为从某个节点X到一个叶子的最短路径长。上图中节点内标示的就是。因此具有0 or 1个儿子的节点的npl是0,定义$npl\; \left( \; null\; \right)\; =\; -1$。那很自然,对于每个节点的npl有如下计算公式:
$npl\left( \; x\; \right)\; =\; \; 1\; +\; \min \left( \; npl\left( \; lc\; \right)\; ,\; npl\; \left( \; rc\; \right)\; \right)$
看上去和某个公式有点眼熟啊,求树高度的算法,和这个很类似,就是把min换成了max,通过类比我们或许对这两个概念能有更深的认识。
有了这个指标,我们就可以以此来度量堆结构的倾斜性。如果左孩子的npl不小于右孩子,就称之为左倾(政治上追求进步2333),如果每个节点都符合这个性质,就称为左倾堆or左式堆。又因为npl定义是在两个孩子中取一个小值+1,那么我们只考虑右边就行了。总结如下:
左倾:对任何节点 $x$,都有$npl\left( \; x->\; lc\; \right)\; \geq \; npl\left( \; x->rc\; \right)$
推论:对任何节点 $x$,都有$npl\left( \; x\; \right)\; =\; 1\; +\; npl\left( \; x->rc\; \right)$
我们也可以推论:左式堆的任何一个子堆也必然是左式堆。第三自然段说过左式堆倾向于节点向左倾斜,但这只是大致的倾向,实际情况不一定都向左。
下面讨论实现,先说合并,然后是插入和删除。
先说一下类型声明
#ifndef LeftHeap_h #define LeftHeap_h struct TreeNode; typedef struct TreeNode *LefHeap; LefHeap Init(); int FindMin(LefHeap H); LefHeap Merge(LefHeap H1,LefHeap H2); //#define Insert(X,H) (H=Insert1((X),H)) void Insert(int x,LefHeap H); int DeleteMin(LefHeap H); LefHeap Insert1(int x,LefHeap H); LefHeap DeleteMin1(LefHeap H); #endif /* LeftHeap_h */ struct TreeNode{ int value; LefHeap left; LefHeap right; int npl; };