左式堆的实现与详解

左式堆的性质:

像二叉堆那样既有结构性质,又有堆序性质。事实上,和所有使用的堆一样,左式堆具有相同的堆序性质,该性质我们已经看到过。不仅如此,左式堆也是二叉树,左式堆和二叉堆唯一的区别是:左式堆不是理想平衡的,而且事实上是趋于非常不平衡的。

零路径长:(null path length)

假设有个结点x,npl(x)定义为x到一个不具有两个儿子的结点的最短路径的长,因此,具有0个或1个儿子的结点的npl为0,而npl(null) = -1.

左式堆的操作:

由于两个左式堆需要合并,所以用数组就显得不是那么的合适了,因为需要把一个数组拷贝到另一个数组中去,对于大小相同的堆这将花费时间O(N),所以我们最好用链式结构来实现左式堆,而不是像二叉堆那样用数组(vector)去实现。

左式堆的插入:

左式堆的插入只是合并的特殊情形,因为可以把插入看成是单结点堆与一个大的堆的merge。

左式堆的合并:

假设有H1,H2两个堆需要合并,那么,如果这两个堆中有一个堆是空的,那么可以直接返回另外一个堆,否则,为了合并这两个堆,需要先比较他们的根,首先将具有大的根值的堆和具有小的根值的堆的右子堆合并。因为左式堆的子堆也应该是左式堆,所以可以利用递归这一性质来进行合并,递归的流程为:先比较两个堆根结点的大小,取根结点值大的堆和根结点值小的右子堆合并,再比较所取右子堆当前根结点和结点较大堆根结点值的大小,然后依次这样比较下去,直到结点不能再分位置,然后就可以开始执行合并,遇到一个堆为空时返回另外一个堆。然后再将其合并到h1的右子树上(h1假设总是根结点较小的那棵树),就这样下去,直到合并完成。

左式堆的删除(Deletemin):

删除根结点后,执行合并根结点的左子树和右子树的方法即可。

Findmin(返回最小值):

堆如果不为空的话,很明显最小值就是根结点,因为满足堆的性质。

左式堆的代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
class UnderflowException
{};
template<typename Comparable>
class LeftistHeap
{
public:
    LeftistHeap():root(NULL)
    {}
    LeftistHeap(const LeftistHeap &rhs)
    {
        *this = rhs;
    }
    ~LeftistHeap()
    {
        makeEmpty();
    }
    bool isEmpty() const;
    const Comparable & findMin() const;
    void insert(const Comparable & x);
    void deleteMin();
    void deleteMin(Comparable & minItem);
    void makeEmpty();
    void merge(LeftistHeap & rhs);
    const LeftistHeap & operator = (const LeftistHeap & rhs)
    {
        if(this != &rhs)
        {
            makeEmpty();
            root = clone(rhs.root);
        }
        return *this;
    }
private:
    struct LeftistNode
    {
        Comparable element;
        LeftistNode *left;
        LeftistNode *right;
        int npl; //零路径长(到不具有两个儿子结点的最小路径)
        LeftistNode(const Comparable &theElement,LeftistNode *lt = NULL,LeftistNode *rt = NULL,int np = 0)
        :element(theElement),left(lt),right(rt),npl(np) {}
    };
    LeftistNode *root;
    LeftistNode *merge(LeftistNode *h1,LeftistNode *h2);
    LeftistNode *merge1(LeftistNode *h1,LeftistNode *h2);
    void swapChildren(LeftistNode *t); //交换左右子树
    void reclaimMemory(LeftistNode *t);
    LeftistNode *clone(LeftistNode *t) const
    {
        if(t == NULL)
            return NULL;
        else
            return new LeftistNode(t->element,clone(t->left),clone(t->right));
    }
};
template<typename Comparable>
void LeftistHeap<Comparable>::merge(LeftistHeap &rhs)
{
    if(this == &rhs)   //避免自己和自己合并
    {
        return;
    }
    root = merge(root,rhs.root);
    rhs.root = NULL; //合并完那棵树就没了
}
template<typename Comparable>
typename LeftistHeap<Comparable>::LeftistNode *LeftistHeap<Comparable>::merge(LeftistNode *h1,LeftistNode *h2)
{
    if(h1 == NULL)
        return h2;
    if(h2 == NULL)
        return h1;
    if(h1->element < h2->element)  //第一个参数为根结点小的那个
        return merge1(h1,h2);
    else
        return merge1(h2,h1);
}
template<typename Comparable>
typename LeftistHeap<Comparable>::LeftistNode *LeftistHeap<Comparable>::merge1(LeftistNode *h1,LeftistNode *h2)
{
    if(h1->left == NULL) //单个结点
    {
        h1->left = h2;
    }
    else  //递归的思路就是 :合并小的左式堆,一直合并,最后变成所要求的左式堆
    {
        h1->right = merge(h1->right,h2); //一直取当前根结点小的h1的右子树和h2合并,然后成为h1的右子树
        if(h1->left->npl <h1->right->npl) //如果合并后破坏了左式堆的性质,那就交换左右子堆
        {
            swapChildren(h1);
        }
        h1->npl = h1->right->npl + 1;
    }
    return h1;
}
template<typename Comparable>
void LeftistHeap<Comparable>::insert(const Comparable &x)
{
    root = merge(new LeftistNode(x),root);
}
template<typename Comparable>
const Comparable &LeftistHeap<Comparable>::findMin() const
{
    if(!isEmpty())
    return root->element;
}
template<typename Comparable>
bool LeftistHeap<Comparable>::isEmpty() const
{
    return root == NULL;
}
template<typename Comparable>
void LeftistHeap<Comparable>::reclaimMemory(LeftistNode *t)
{
    if(t != NULL)
    {
        reclaimMemory(t->left);
        reclaimMemory(t->right);
        delete t;
    }
}
template<typename Comparable>
void LeftistHeap<Comparable>::makeEmpty()
{
    reclaimMemory(root);
    root = NULL;
}
template<typename Comparable>
void LeftistHeap<Comparable>::swapChildren(LeftistNode *t) //如果破坏了左式堆的性质,那么就交换根结点的左右子堆
{
    LeftistNode *tmp = t->left;
    t->left = t->right;
    t->right = tmp;
}
template<typename Comparable>
void LeftistHeap<Comparable>::deleteMin()
{
    if(isEmpty())
    {
        throw UnderflowException();
    }
    LeftistNode *oldRoot = root;
    root = merge(root->left,root->right); //删除根结点,让左子堆和右子堆合并
    delete oldRoot;
}
template<typename Comparable>
void LeftistHeap<Comparable>::deleteMin(Comparable & minItem)
{
    minItem = findMin();
    deleteMin();
}
int main()
{
    LeftistHeap<int> leftistHeap1,leftistHeap2;
    cout<<"enter first heap,total m numbers:"<<endl;
    int m,n,num;
    //freopen("111","r",stdin);
    cin>>m;
    for(int i=0;i<m;i++)
    {
        cin>>num;
        leftistHeap1.insert(num); //第一个堆
    }
    cout<<"enter first heap,total n numbers:"<<endl;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>num;
        leftistHeap2.insert(num); //第二个堆
    }
    leftistHeap1.merge(leftistHeap2);
    cout<<"minimum is:"<<leftistHeap1.findMin()<<endl;
    if(leftistHeap1.isEmpty())
    {
        cout<<"leftistHeap1 is empty!"<<endl;
    }
    else
    {
        cout<<"leftistHeap1 is not empty!"<<endl;
    }
    if(leftistHeap2.isEmpty())
    {
        cout<<"leftistHeap2 is empty!"<<endl;
    }
    else
    {
        cout<<"leftistHeap2 is not empty!"<<endl;
    }
    int t;
    while(leftistHeap1.isEmpty() == false)
    {
        leftistHeap1.deleteMin(t);
        cout<<t<<" ";
    }
    cout<<endl;
    if(leftistHeap1.isEmpty())
    {
        cout<<"leftistHeap1 is empty!"<<endl;
    }
    else
    {
        cout<<"leftistHeap1 is not empty!"<<endl;
    }
    return 0;
}