左式堆的基本操作

摘要:二叉堆并不支持高效率的合并,因为这需要把一个数组拷贝到另外一个数组里面,这需要消耗O(N)的时间.实际上,所有高效支持合并的操作都是用指针来进行的.

左式堆也是一种二叉堆,它与普通二叉堆唯一的区别就是它是不平衡的二叉堆,实际上是非常不平衡.把一个节点的零路径(Npl)长定义为一个节点到一个没有两个儿子的节点的最短路径,因此具有一个儿子或者没有儿子的树的零路径长是0.而NULL节点的npl是-1.
注意,任意一节点的npl比它儿子的中最小npl多1.
左式堆的性质是:对于任意一个节点,左儿子的npl至少和右儿子一样大.
这个性质保证了右子树尽可能的短,所以将大多数工作都放在右子树上去做.

【1】基本数据结构

struct TreeNode
{
    int Element;
    int Npl;
    LeftHeap Left;
    LeftHeap Right;
};

【2】合并操作:
合并两个左式堆要保证不能破坏左式堆的性质.
{1}如果两个堆有一个是空的,则我们返回不为空的堆;
{2}否则,比较它们的根,将具有较大的根和较小根的右子树递归的合并.基础情况(根为空的时候)显然成立,如果中间步骤成立,那么该算法的正确性就可以被证明了.注意到该合并并不会破坏堆序(因为总是选取较小的根的右子树进行合并),这样较大的根就会被接在较小根的右子树上面。
但是它的左式堆的性质可能被破坏.而根的左子树由于并没有参与合并,因此不会被破坏。由于递归步骤保证了右子树也是左式的。因此只需要对左右子树进行交换就可以了,这样根处的不满足就被消除了.

LeftHeap Merge(LeftHeap H1,LeftHeap H2)
{
    if(!(H1&&H2))//返回不为空的H(可能都是NULL?)
        return H1==NULL?H2:H1;
    if(H1->Element>H2->Element)
        return Merge1(H2,H1);
    else if (H1->Element<H2->Element)
        return Merge1(H1,H2);//H1为合并的主干
    else
    {
        puts("error: there should not be any same element between two heap");
        return NULL;
    }
}
LeftHeap Merge1(LeftHeap H1,LeftHeap H2)
{
    if(H1->Left==NULL)//H1->Right也是NULL,基准情况之一
        H1->Left = H2;
    else
    {
        H1->Right = Merge(H1->Right,H2);
        if(H1->Right->Npl > H1->Left->Npl)
            swapchildren(H1);
        H1->Npl = H1->Right->Npl+1;
    }
    return H1;
}
void swapchildren(LeftHeap H)
{
    LeftHeap temp;
    temp = H->Left;
    H->Left = H->Right;
    H->Right = temp;
}

【3】插入操作:
可以看做特殊的合并

LeftHeap Insert1(int x,LeftHeap H)
{
    LeftHeap singleNode = (LeftHeap)malloc(sizeof(TreeNode));
    singleNode->Element = x;
    singleNode->Left = NULL;
    singleNode->Right = NULL;
    singleNode->Npl = 0;
    return Merge(H,singleNode);
}

你可能感兴趣的:(指针)