zoukankan      html  css  js  c++  java
  • 优先队列-左式堆

    一、基础性质

    零路径长:任一节点的零路径长 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: LeftistHeapIncLeftistHeap.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
  • 相关阅读:
    WebView Android 调用js且须要获取返回结果
    推荐系统--揭开推荐的神奇面纱
    回调函数
    对CAB文件进行数字签名
    adodb.RecordSet的属性和方法
    Code Review中的几个提示
    Linux下find命令具体解释
    html5中关于input使用方法的改变
    关于 ioctl 的 FIONREAD 參数
    Grant的时候报错的解决:Access denied for user &#39;root&#39;@&#39;localhost&#39; (using password: YES)
  • 原文地址:https://www.cnblogs.com/lsyy2020/p/12662735.html
Copyright © 2011-2022 走看看