左式堆的基本操作

什么是左式堆

左式堆是一种有效地支持堆的合并操作的高级数据结构。左式堆既有二叉堆的结构性质,又有堆序性质。但和二叉堆不同,左式堆同时也是一棵二叉树,而二叉堆可以简化为数组。左式堆不是理想平衡的,事实上它也是趋于不平衡的。

左式堆的性质

我们把任一节点X的零路径长(null path length)npl(x)定义为从X节点到一个不具有两个儿子的节点的最短路径的长度。
因此,具有0个或1个孩子的节点的npl值为0,而npl(null)= -1。比如如下这棵树,节点里的数字表示该节点的零路径长。

                                 1                       1    
                                /  \                    / \
                              1     0                  1*   0 
                             / \                      / \            
                            0   0                    0   1
                               /                        / \
                              0                        0   0
                                k1                       k2

这里可以简单概括一个规律:任一节点的零路径长(npl)等于它的两个儿子中的最小值+1。

而左式堆的性质是:对于堆中的任一节点X,左儿子的零路径长总大于或等于右儿子的零路径长。
比如上图,k1是左式堆而k2不是,因为k2中打*的节点左儿子的npl比右儿子小。

因此对于左式堆,结合左式堆的性质和总结出的规律,可以得到:左式堆中任一节点的零路径长等于右儿子的零路径长+1,即 npl(t) = npl(t->right) + 1。这对于没有右孩子的节点同样适用,因为npl(NULL) = -1

左式堆的操作

#include<iostream>
using namespace std;

template<typename T>
class LeftistNode{
public:
    T element;      
    LeftistNode*left;
    LeftistNode*right;
    int npl;        //记录当前节点的npl值

public:
    LeftistNode(int e, LeftistNode*l = NULL, LeftistNode*r = NULL, int n=0) 
        :element(e), left(l), right(r),npl(n){}

};


template<typename T>
class LeftistHeap{
private:
    LeftistNode<T>* root;

public:
    LeftistHeap(const T & x){
        root = new LeftistNode<T>(x);
    }
    LeftistHeap(){}
    bool isEmpty(){
        return root == NULL;
    }
    const T & findMin()const{
        return root->element;
    }

    void insert(const T & x){
        root = merge(new LeftistNode<int>(x), root);
    }
    /*
    特殊的删除方法
    删除根节点
    把剩余的两个堆合并
    */
    void deleteMin(){
        if (isEmpty()){
            return;
        }
        LeftistNode* old = root;
        root = merge(root->left, root->right);
        delete old;
    }
    void deleteMin(T & x){
        x = findMin();
        deleteMin();
    }
    //合并两个堆
    void merge(LeftistHeap & rhs){
        if (this == &rhs){
            return;
        }
        this->root = merge(root, rhs.root);
        rhs.root = NULL;
    }

    void printTree()const{
        printTree(root);
        cout << endl;
        printTree(root, 1);
        cout <<endl;
    }

private:

    /*
    对两个堆的情况分类处理
    不分先后
    如果有一个是空堆就返回另一个堆
    否则调用merge1()
    */
    LeftistNode<T> * merge(LeftistNode<T> * h1, LeftistNode<T> * h2){
        if (h1 == NULL){
            return h2;
        }
        if (h2 == NULL){
            return h1;
        }
        if (h1->element < h2->element){
            return merge1(h1, h2);
        }
        if(h1->element > h2->element){
            return merge1(h2, h1);
        }
    }
    //默认h1的根比h2小
    LeftistNode<T>* merge1(LeftistNode<T>* h1, LeftistNode<T>* h2){

        //如果左子树为空
        //就把h2接上去
        if (h1->left == NULL){
            h1->left = h2;
        }

        else{
            //把右子树和h2做比较,递归调用merge()合并
            h1->right = merge(h1->right, h2);
            //合并完成后检验根节点是否为左式堆
            //如果不是左式堆就交换两个孩子
            if (h1->left->npl < h1->right->npl){
                swapChildren(h1);
            }
            //重置根的npl值
            h1->npl = h1->right->npl + 1;
        }
        return h1;
    }
    //交换两个子树
    void swapChildren(LeftistNode<T>*t){
        LeftistNode<T> * temp = t->left;
        t->left = t->right;
        t->right = temp;
    }

    void printTree(const LeftistNode<T>* t)const{
        if (t != NULL){
            printTree(t->left);
            cout << t->element << ' ';
            printTree(t->right);
        }
    }
    void printTree(const LeftistNode<T>* t,int x)const{
        if (t != NULL){
            printTree(t->left,1);
            printTree(t->right,1);
            cout << t->element << ' ';
        }
    }

};

你可能感兴趣的:(C++)