优先队列-左式堆

一、基础性质

零路径长:任一节点的零路径长 npl( X ) 定义为从X到一个不具有两个儿子的节点的最短路径长。

注意:具有 0 个或 1 个儿子的节点的 npl 为 0 ,而 npl( nullptr ) = -1

        图1 两棵树的零路径长;只有左边的树是左式堆

左式堆性质:对于堆中的每个节点X,左儿子的零路径长至少与右儿子路径长一样。上图中,即左边的那棵树满足性质。这个性质实际上超出了它确保树不平衡的要求,因此它显然偏重于向左增加深度。确实有可能存在有左节点形成的长路径构成的树——因此,我们就有了名称左式堆

定理:N个节点的左式树有一条最多含有 log( N + 1 ) 个节点的右路径

右路径:节点的右儿子的右儿子的右儿子......组成的路径。


 

二、左式堆操作:

1.合并( merge ) 

时间复杂度O( log N )

具体分三种情况(设堆H1的根值小于H2)

 

 

 

 

 

 

    // 合并两个根
    LeftistNode* merge( LeftistNode* h1, LeftistNode* h2 )
    {
        if( h1 == nullptr )
            return h2;
        if( h2 == nullptr )
            return h1;
        if( h1->element < h2->element )
            return merge1( h1, h2 );
        else
            return merge1( h2, h1 );
    }
    // 合并两个左式树 h1 < h2
    LeftistNode* merge1( LeftistNode* h1, LeftistNode* h2 )
    {
        // 单节点
        // 左式堆如果 h->left = nullptr, 则 h->right = nullptr
        if( h1->left == nullptr )           
            h1->left = h2;
        else
        {
            // 递归合并根节点小的右子树与根节点大的树
            h1->right = merge( h1->right, h2 );        
            // 左子树的零路径长小于右子树的零路径长时交换左右子树
            if( h1->left->npl < h1->right->npl )
                swapChildren( h1 );
            // 根节点的零路径长 = 右子树零路径长 + 1 
            h1->npl = h1->right->npl + 1; 
        }

        return h1;
    }

 

2.插入( insert )

把插入看成是单节点堆与一个更大的堆的merge。

    // 插入节点 x
    void insert( const Comparable & x )
    {
        root = merge( new LeftistNode{ x }, root );
    }

 

3.删除( delete )

把删除看成合并根节点的左子树和根节点的右子树,然后再删除根节点。

    // 删除最小项
    void deleteMin( )
    {
        if( isEmpty( ) )
           throw UnderflowException{ };
        
        LeftistNode* oldRoot = root;
        root = merge( root->left, root->right );
        delete oldRoot;
    }
    // 删除最小值
    void deleteMin( Comparable & minItem )
    {
        minItem = findMin( );
        deleteMin( );
    }

 


 

三、代码

/*
 * @Author: your name
 * @Date: 2020-02-15 16:08:15
 * @LastEditTime: 2020-02-19 20:22:38
 * @LastEditors: Please set LastEditors
 * @Description: 左式堆
 * @FilePath: \LeftistHeap\Inc\LeftistHeap.h
 */

#ifndef __LEFTISTHEAD_H
#define __LEFTISTHEAD_H


#include <vector>
#include <iostream>
#include "dsexceptions.h"

template < typename Comparable >
class LeftistHeap
{
public:
    LeftistHeap( ) : root{ nullptr }
    {

    }
    LeftistHeap( const LeftistHeap & rhs ): root( nullptr )
    {
        this->root = clone( rhs.root );
    }
    ~LeftistHeap( )
    {
        makeEmpty( );
    }
    // 深拷贝
    LeftistHeap & operator = ( const LeftistHeap & rhs )
    {
        LeftistHeap copy = rhs;
        std::swap( *this, copy );
        return *this;
    }
    // 移动
    LeftistHeap & operator = ( LeftistHeap && rhs )
    {
        std::swap( root, rhs.root );
        
        return *this;
    }

    // 判断左式堆是否为空
    bool isEmpty( ) const
    {
        return root == nullptr;
    }
    // 寻找最小值
    const Comparable & findMin( )   const
    {
        if( isEmpty( ) )
            throw UnderflowException{ };
        return root->element;
    }
    // 插入节点 x
    void insert( const Comparable & x )
    {
        root = merge( new LeftistNode{ x }, root );
    }
    // 删除最小项
    void deleteMin( )
    {
        if( isEmpty( ) )
           throw UnderflowException{ };
        
        LeftistNode* oldRoot = root;
        root = merge( root->left, root->right );
        delete oldRoot;
    }
    // 删除最小值
    void deleteMin( Comparable & minItem )
    {
        minItem = findMin( );
        deleteMin( );
    }
    void makeEmpty( )
    {
        reclaimMemory( root );
        root = nullptr;
    }
    // 将 rhs 合并到优先队列
    void merge( LeftistHeap & rhs )
    {
        if( this == &rhs )                      // rhs 必须不同于 this
            return;
        root = merge( root, rhs.root );
        rhs.root = nullptr;
    }
    void print( )   const{
        print( root );
    }

private:
    struct LeftistNode
    {
        Comparable element;
        LeftistNode* left;
        LeftistNode* right;
        int npl;                // 零路径长
        LeftistNode( const Comparable & e, LeftistNode* lt = nullptr, 
                    LeftistNode* rt = nullptr, int np = 0 )
        : element{ e }, left{ lt }, right{ rt }, npl{ np }
        {

        }

    };

    LeftistNode* root;          // 根节点
    // 合并两个根
    LeftistNode* merge( LeftistNode* h1, LeftistNode* h2 )
    {
        if( h1 == nullptr )
            return h2;
        if( h2 == nullptr )
            return h1;
        if( h1->element < h2->element )
            return merge1( h1, h2 );
        else
            return merge1( h2, h1 );
    }
    // 合并两个左式树 h1 < h2
    LeftistNode* merge1( LeftistNode* h1, LeftistNode* h2 )
    {
        // 单节点
        // 左式堆如果 h->left = nullptr, 则 h->right = nullptr
        if( h1->left == nullptr )           
            h1->left = h2;
        else
        {
            // 递归合并根节点小的右子树与根节点大的树
            h1->right = merge( h1->right, h2 );        
            // 左子树的零路径长小于右子树的零路径长时交换左右子树
            if( h1->left->npl < h1->right->npl )
                swapChildren( h1 );
            // 根节点的零路径长 = 右子树零路径长 + 1 
            h1->npl = h1->right->npl + 1; 
        }

        return h1;
    }
    // 交换一个节点的左右儿子
    void swapChildren( LeftistNode* t )
    {
        LeftistNode* tempNode = t->left; 
        t->left = t->right;
        t->right = tempNode;
    }
    // 清空某子树
    void reclaimMemory( LeftistNode* t )
    {
        if( t != nullptr )
        {
            reclaimMemory( t->left );
            reclaimMemory( t->right );
            delete t;
        }
    }
    // 克隆节点
    LeftistNode* clone( LeftistNode* t )    const
    {
        if( t == nullptr )
            return nullptr;
        else
            return new LeftistNode{ t->element, clone( t->left ), clone( t->right ), t->npl };
        
    }
    void print( LeftistNode *t )   const{
        if( t != nullptr ){
            print( t->left );
            std::cout << t->element <<" ";
            print( t->right );
        }
    }
};





#endif

 

posted @ 2020-04-08 20:57  白小白2020  阅读(245)  评论(0编辑  收藏  举报