• 本文代码下载:

    • 方式1:公众号【多栖技术控小董】回复【3586】免费获取下载链接
    • 方式2:CSDN下载链接:https://download.csdn.net/download/qq_41453285/12046050
    • 方式3:Github下载链接(进入之后下载里面的maxHblt.zip文件):https://github.com/dongyusheng/Interview-algorithm/tree/master/c%2B%2BAlgorithms
  • 前面文章介绍了用堆实现优先级队列:https://blog.csdn.net/qq_41453285/article/details/103639243
  • 用堆实现优先级队列的空间利用率很高,而且效率也很高,但是并不适用于所有优先级队列,尤其当两个优先级队列或多个长度不同的队列需要合并的时候,这时就需要其他的数据结构了,左高树就能满足这种需要
  • 堆与左高树的异同:
    • WBLT、HBLT可以执行优先级队列的查找、插入、删除操作,时间复杂度与堆相同
    • WBLT、HBLT和堆一样,可以在线性时间内完成初始化
    • 用WBLT、HBLT表示的两个优先级队列可以在对数时间内合并为一个,而桶堆表示的优先级队列却不行

一、扩充二叉树

  • 如果一棵二叉树,有一类特殊的节点叫做外部节点,它替代树中的空子树。其余节点叫做内部节点
  • 增加了外部节点的二叉树称为扩充二叉树
  • 例如下图a是一棵二叉树,下图b是图a的扩充二叉树(外部节点用[a-f]表示)

高度s的定义

  • 令s(x)是节点x到其子树的外部节点的所有路径中最短的一条,则有:

    • 若x是外部节点,则s(x)=0
    • 若x是内部节点,则s(x)=min{s(L),s(R)+1},其中L、R分别代码左、右孩子
  • 例如,下面的扩充二叉树(图b),每个节点的s值如图c所示

重量w的定义

  • 如果不以路径长度s来对左高树进行定义,而是以节点数目来定义,那么就可以得到另一种左高树
  • 节点的重量(w):
    • 若x是外部节点,则w(x)=0
    • 若x是内部节点,则w(x)=w(子节点的重量)+1
  • 例如,下面的扩充二叉树,每个节点的w值如下图所示

二、高度优先左高树(HBLT)

  • 定义:当且仅当其任何一个内部节点的左孩子的s值都大于或等于右孩子的s值,称为高度优先左高树(height-biased leftist tree,HBLT)
  • 例如下图就是一个高度优先左高树(HBLT),其中阴影部分代表外部节点

定理

  • 令x为HBLT的一个内部节点,则有:

    • 1).以x为根的子树的节点数目至少为
    • 2).若以x为根的子树有m个节点,那么s(x)最多为
    • 3).从x到一外部节点的最右路径(即从x开始沿右孩子移动的路径)的长度为s(x)

最大HBLT、最小HBLT

  • 若一棵HBLT同是还是大根树,那么称之为最大HBLT(max HBLT),可用于实现最大优先级队列
  • 若一棵HBLT同是还是小根树,那么称之为最小HBLT(min HBLT),可用于实现最小优先级队列
  • 例如下图3个都是最大HBLT

  • 例如下图3个都是最小HBLT

最大HBLT的插入

  • 最大HBLT的插入操作可利用最大HBLT的合并操作来实现
  • 插入的策略:
    • 1.假设元素x要插入名为H的最大HBLT中
    • 2.先建立一棵新的只包含x元素的HBLT
    • 3.然后将这棵新的HBLT与名为H的HBLT进行合并
    • 4.合并之后的HBLT就为插入之后最终的HBLT

最大HBLT的删除

  • 最大HBLT与大根堆一样,最大元素在根中。因此删除操作也是删除根节点
  • 删除操作也是利用最大HBLT的合并操作来实现
  • 删除的策略:
    • 1.若根被删除,则分别以根节点的左右孩子节点为根的子树是两棵最大HBLT
    • 2.然后将这棵最大HBLT合并,之后便是删除后的结果

两棵HBLT的合并

  • 具有n个元素的HBLT,其最右路径的长度为O()。一个算法要合并两棵HBLT,只能在遍历其最右路径中进行。由于在每个节点上实施合并所需时间为O(1),所以合并算法的时间复杂性是合并后节点数的对数。因此算法从两棵HBLT的根开始仅沿右孩子移动
  • 合并的策略:
    • 1.假设A、B是两棵需要合并的最大HBLT。如果一者为空,则另一个便是合并的结果;如果两者均不为空,则进行以下步骤
    • 2.先比较两个HBLT的根,较大者的根作为合并后的HBLT的根
    • 3.假设A的根大于B,且A的左子树为L
    • 4.然后将A的右子树与B合并,然后形成一棵名为C的HBLT(如果A没有右子树,那B就什么都不做)
    • 5.然后将C与A合并
    • 6.如果L的s值小于C的s值,则以C为左子树,L为右子树;否则,L为左子树,C为右子树
    • 合并的策略需要使用递归来实现(因为通过上面的步骤我们知道,如果将A的右子树与B进行合并,也是一次HBLT合并的操作,因此是递归的特性)
  • 下面的演示案例中:每个节点的s值标在节点的外部,元素的值标在节点的里面。并且根较大的树我们放置在左边,右边的HBLT用阴影表示
  • 演示案例1:
    • 1.假设a)是两棵想要合并的HBLT
    • 2.因为9的右子树为空,所以就不存在9的右子树与7进行合并的操作
    • 3.然后将7与9为根的HBLT进行合并(图b)所示)
    • 4.合并之后9的左子树的s值为0(也就是外部节点的s值),而右子树(7)的s值为1,因此将7作为9的左子树。最终合并的结果如c)所示

  • 演示案例2:

    • 1.假设d)是两棵想要合并的HBLT
    • 2.因为10的右子树为空,所以就不存在10的右子树与7进行合并的操作
    • 3.然后将7与9为根的HBLT合并(如图e)所示)
    • 4.合并之后检查10的左子树的s值与右子树的s值相等,因此就不交换。最终的结果如图e)所示

  • 演示案例3:

    • 1.假设f)是两棵想要合并的HBLT
    • 2.18的右子树不为空,因此需要将18的右子树与10为根的HBLT进行合并,合并之后的结果如上图e)所示
    • 3.18的右子树与10为根的HBLT合并之后产生一个新的HBLT,然后将这个HBLT作为18的右子树(如图g)所示)
    • 4.合并之后,然后开始比较18的左右子树的s值,比较之后,发现18的右子树的s值比左子树的s值大,因此需要将左右子树进行互换。互换之后的结果如h)所示,这也是最终的合并结果

  • 演示案例4:

    • 1.假设i)是两棵想要合并的HBLT
    • 2.因为40的右子树不为空,因此将40的右子树与以18为根的HBLT进行合并,合并的结果就是我们上面所介绍的h)所示
    • 3.合并之后将h)作为20的右子树(如图j)所示)
    • 4.合并之后,然后开始比较40的左右子树的s值,比较之后,发现40的右子树的s值比左子树的s值大,因此需要将左右子树进行互换。互换之后的结果如k)所示,这也是最终的合并结果

最大HBLT的初始化

  • 初始化过程是将n个元素逐个插入最初为空的最大HBLT,所需时间为O()。并且也用到了合并操作
  • 初始化的策略:
    • 1.首先创建n个仅包含一个元素的最大HBLT,将这n棵树组成一个FIFO队列
    • 2.然后从队列中依次成对删除HBLT,然后将其合并后再插入队列末尾,直到队列只有一棵HBLT位置
  • 演示案例:
    • 1.假设我们希望构造一棵最大HBLT,其具有5个元素:7、1、9、11、2
    • 2.先将这5个元素做个分别作为5棵单独的HBLT,然后组成一个FIFO队列
    • 3.接着从队列中取出一对HBLT进行合并(上面可以看出是7和1这两个HBLT),将7和1进行合并(如图a)所示)
    • 4.然后将7和1组成的HBLT放入到队列的尾部
    • 5.接着从队列头部再次取出两棵HBLT,此时为9和11,然后将9和11组成一棵HBLT(如图b)所示)
    • 6.然后将9和11组成的HBLT放入到队列的尾部(注意,此时队列中只有3棵HBLT了)
    • 7.然后从队列中再取出两棵HBLT进行合并,此时取出的是1和7、2组成的HBLT,然后将两者合并(如图c所示)
    • 8.然后将合并后的结果放入到队列尾部(注意,此时队列中只有2棵HBLT了)
    • 9.最后队列中只有两棵HBLT了(就是c)和b)),然后将b)和c)进行合并,最终的结果如d)所示

二、重量优先左高树(WBLT)

  • 定义:当且仅当其任何一个内部节点的左孩子的w值都大于或等于其右孩子的w值,称为重量优先左高树(wieght-biased leftist tree,WBLT)
  • 最大HBLT、最小HBLT:
    • 若一棵WBLT同是还是大根树,那么称之为最大WBLT(max WBLT),可用于实现最大优先级队列
    • 若一棵WBLT同是还是小根树,那么称之为最小WBLT(min WBLT),可用于实现最小优先级队列
  • 与HBLT异同:
    • 同HBLT类似,具有m个节点的WBLT的最右路径长度最多为
    • WBLT的查找、插入、删除、合并、初始化和HBLT的类似,所以可以参考上面HBLT的操作

三、编码实现(最大HBLT)

头文件定义

#include <iostream>
#include <string>
#include <queue>using std::cout;
using std::endl;
using std::cin;
using std::string;
using std::pair;
using std::queue;

异常类定义

class illegalParameterValue
{string message;
public:illegalParameterValue(const char *theMessage = "Illegal Parameter Value") :message(theMessage) {}const char *what() {return message.c_str();}
};class queueEmpty
{
public:queueEmpty(string theMessage = "Invalid operation on empty queue"){message = theMessage;}const char *what() {return message.c_str();}
private:string message;
};

树节点定义

  • 用来表示树中的每一个节点
template<typename T>
struct binaryTreeNode
{T element;struct binaryTreeNode<T>* leftChild, *rightChild;binaryTreeNode() { this->leftChild = this->rightChild = nullptr; }binaryTreeNode(const T& theElement) :element(theElement) {this->leftChild = this->rightChild = nullptr;}binaryTreeNode(const T& theElement,const struct binaryTreeNode<T>* theLeftChild,const struct binaryTreeNode<T>* theRightChild) :element(theElement) {this->leftChild = theLeftChild;this->rightChild = theRightChild;}
};

抽象类定义

template<typename T>
class maxPriorityQueue
{
public:virtual ~maxPriorityQueue() {}virtual bool empty()const = 0;//当队列为空返回true;否则返回falsevirtual int size()const = 0;//返回队列的元素个数virtual const T& top() = 0;//返回优先级最大的元素的引用virtual void pop() = 0; //删除队首元素virtual void push(const T& theElement) = 0;//插入元素theElement
};

主类定义

  • 主类继承于maxPriorityQueue抽象类
  • 其中每个树节点类型为std::pait<int,T>类型
    • int:代表每个节点的s值
    • T:代表每个节点的数值
template<typename T>
class maxHblt :public maxPriorityQueue<T>
{
public:maxHblt() { this->root = nullptr; this->treeSize = 0;}~maxHblt() { erase(); }bool empty()const override {//当队列为空返回true;否则返回falsereturn this->treeSize == 0;}int size()const override {  //返回队列的元素个数return this->treeSize;}const T& top()override {     //返回优先级最大的元素的引用if (this->treeSize == 0)throw queueEmpty();return this->root->element.second;}void pop()override;                    //删除队首元素void push(const T& theElement)override;//插入元素theElementvoid initialize(T* theElement, int theSize);//初始化一个HBLTvoid erase(); //清空树void meld(maxHblt<T>& theHblt); //将本棵HBLT与参数所指的HBLT进行合并,内部调用私有方法meldvoid preOrderOutput()const { preOrder(this->root); }void inOrderOutput()const { inOrder(this->root); }void postOrderOutput()const { postOrder(this->root); }private:void preOrder(binaryTreeNode<std::pair<int, T>>* x)const;void inOrder(binaryTreeNode<std::pair<int, T>>* x)const;void postOrder(binaryTreeNode<std::pair<int, T>>* x)const;void clearTree(binaryTreeNode<std::pair<int, T>>* t);void meld(binaryTreeNode<std::pair<int,T>>* &x, binaryTreeNode<std::pair<int,T>>*& y);
private:binaryTreeNode<std::pair<int,T>>* root; //根节点int treeSize;            //树的大小
};

meld函数

  • 根据上面介绍的HBLT合并原理,定义了此函数,用来合并两棵HBLT树
//将x和y两棵HBLT合并
template<typename T>
void maxHblt<T>::meld(binaryTreeNode<std::pair<int, T>>* &x, binaryTreeNode<std::pair<int, T>>*& y)
{//如果y为空,不进行合并if (y == nullptr)return;//如果x为空,那么就将y赋值给xif (x == nullptr){x = y;return;}//如果x的值小于y的值,进行交换if (x->element.second < y->element.second)std::swap(x, y);//将x的右子节点与y合并。如果x没有右子节点,那么就将y设置为x的右子节点meld(x->rightChild, y);//如果x的左子节点为空,将右子节点设置为左子节点if (x->leftChild == nullptr) {x->leftChild = x->rightChild;x->rightChild = nullptr;//因为把右子节点赋值给左子节点了,所以右子节点为空了,那么本节点的s值就为1了x->element.first = 1;}else //如果左子节点不为空,比较是否需要交换{//如果左子节点的s值比右子节点的小,那么就进行交换if (x->leftChild->element.first < x->rightChild->element.first) {std::swap(x->leftChild, x->rightChild);}//因为右子节点到外部节点之间的s值是最小的,所以就将x的s值设置为右子节点的s值+1x->element.first = x->rightChild->element.first + 1;}
}

pop、push函数定义

  • 根据上面最大HBLT的删除、原理定义了下面的函数,都调用了meld合并函数
template<typename T>
void maxHblt<T>::pop()
{//如果HBLT为空,不能出队列if (this->treeSize == 0)throw queueEmpty();//得到根节点的左右子节点binaryTreeNode<std::pair<int, T>>* left = this->root->leftChild,*right=this->root->rightChild;//删除根节点,将左子节点变为新根节点,然后进行合并delete this->root;this->root = left;meld(this->root, right);this->treeSize--;
}template<typename T>
void maxHblt<T>::push(const T& theElement)
{//创建一个新节点binaryTreeNode<std::pair<int, T>> *q = new binaryTreeNode<std::pair<int, T>>(std::pair<int,T>(1, theElement));//将新节点与本HBLT进行合并this->meld(this->root,q);this->treeSize++;
}

erase、clearTree函数

  • erase用来清空整个HBLT树,调用了clearTree函数
template<typename T>
void maxHblt<T>::erase()
{//调用clearTreeclearTree(this->root);this->root = nullptr;this->treeSize = 0;
}template<typename T>
void maxHblt<T>::clearTree(binaryTreeNode<std::pair<int, T>>* t)
{//后序遍历删除if (t){clearTree(t->leftChild);clearTree(t->rightChild);delete t;}
}

initialize函数

  • 根据上面介绍的FIFO初始化HBLT原则,定义了此函数
template<typename T>
void maxHblt<T>::initialize(T* theElement, int theSize)
{//创建一个队列,用来初始化HBLTstd::queue<binaryTreeNode<std::pair<int, T>>*> q;//清空当前HBLTerase();//先建立一组HBLT,每个HBLT中只有一个节点for (int i = 1; i <=theSize; ++i)q.push(new binaryTreeNode<std::pair<int, T>>(std::pair<int, T>(1, theElement[i])));//theSize个HBLT,需要合并theSize-1次for (int i = 1; i <= theSize - 1; ++i){//从队列中取出两个HBLT进行合并binaryTreeNode<std::pair<int, T>>* b = q.front();q.pop();binaryTreeNode<std::pair<int, T>>* c = q.front();q.pop();//合并meld(b, c);//合并之后再次放入到队列中q.push(b);}if (theSize > 0)this->root = q.front();this->treeSize = theSize;
}

打印函数

  • 分别为前序遍历、中序遍历、后序遍历
template<typename T>
void maxHblt<T>::preOrder(binaryTreeNode<std::pair<int, T>>* x)const
{if (x){std::cout << x->element.second<<" ";preOrder(x->leftChild);preOrder(x->rightChild);}
}template<typename T>
void maxHblt<T>::inOrder(binaryTreeNode<std::pair<int, T>>* x)const
{if (x){inOrder(x->leftChild);std::cout << x->element.second << " ";inOrder(x->rightChild);}
}template<typename T>
void maxHblt<T>::postOrder(binaryTreeNode<std::pair<int, T>>* x)const
{if (x){postOrder(x->leftChild);postOrder(x->rightChild);std::cout << x->element.second << " ";}
}

复杂度分析

演示案例1

int main()
{maxHblt<int>* myMaxHblt = new maxHblt<int>();myMaxHblt->push(7);myMaxHblt->push(5);myMaxHblt->push(10);myMaxHblt->push(3);myMaxHblt->preOrderOutput();std::cout << std::endl;myMaxHblt->inOrderOutput();std::cout << std::endl;myMaxHblt->postOrderOutput();std::cout << std::endl;std::cout << std::endl;myMaxHblt->pop();myMaxHblt->preOrderOutput();std::cout << std::endl;myMaxHblt->inOrderOutput();std::cout << std::endl;myMaxHblt->postOrderOutput();std::cout << std::endl;std::cout << std::endl;return 0;
}
  • 演示结果如下图所示:

  • 根据插入原则,其步骤如下

  • 当pop一个元素之后(删除根节点10),其步骤如下

演示案例2

int main()
{maxHblt<int>* myMaxHblt = new maxHblt<int>();int arr[5] = { 0,7,5,10,3 };myMaxHblt->initialize(arr,4);myMaxHblt->preOrderOutput();std::cout << std::endl;myMaxHblt->inOrderOutput();std::cout << std::endl;myMaxHblt->postOrderOutput();std::cout << std::endl;std::cout << std::endl;myMaxHblt->pop();myMaxHblt->preOrderOutput();std::cout << std::endl;myMaxHblt->inOrderOutput();std::cout << std::endl;myMaxHblt->postOrderOutput();std::cout << std::endl;std::cout << std::endl;return 0;
}
  • 这个演示案例调用了intialize函数,元素与演示案例1都是一样的,结果应该与演示案例1一样(备注:但是不知道为什么,结果与演示案例1不一样,待更新)

C++(数据结构与算法):42---优先级队列的实现(扩充二叉树、高度优先左高树(HBLT)、重量优先左高树(WBLT))相关推荐

  1. 数据结构与算法之循环队列的操作

    数据结构与算法之循环队列的操作 /* 循环队列的入队和出队算法设计 初始化循环队列 .打印队列.插入元素到循环队列.获取循环队列的首元素,元素不出队.出队.获取循环队列元素个数.判断循环队列的空和满. ...

  2. python 熊猫钓鱼_Python数据结构与算法之使用队列解决小猫钓鱼问题

    本文实例讲述了Python数据结构与算法之使用队列解决小猫钓鱼问题.分享给大家供大家参考,具体如下: 按照<啊哈>里的思路实现这道题目,但是和结果不一样,我自己用一幅牌试了一下,发现是我的 ...

  3. 数据结构与算法-栈与队列

    数据结构与算法-栈与队列 栈 基本概念 简单表述就是仅在表尾进行插入和删除操作的线性表. 常见操作 入栈和出栈, 均在线性表的尾部进行. 基本原则就是, 先入后出. 队列 基本概念 和栈不同的是,队列 ...

  4. 【数据结构Python描述】优先级队列描述“银行VIP客户插队办理业务”及“被插队客户愤而离去”的模型实现

    文章目录 一.支持插队模型的优先级队列 队列ADT扩充 队列记录描述 方法理论步骤 `update(item, key, value)` `remove(item)` 二.支持插队模型的优先级队列实现 ...

  5. char栈java,Java数据结构与算法-栈和队列(示例代码)

    (摘录加总结)------ 栈和队列不属于基础的数据结构,它们都属于线性表. 一.栈 对于栈存储操作元素只能在栈结构的一端进行元素的插入和删除,是一种性质上的线性表结构.按照"先进后出&qu ...

  6. 2.2基本算法之递归和自调用函数_数据结构与算法之5——队列和栈

    栈和队列比较简单,而且实用性非常广泛,这里主要介绍一下他们的概念和实现,在很多算法中,栈和队列的运用很重要,因此,虽然简单确是最重要的数据结构之一,必须重视. 栈是保证元素后进先出(后存入者先使用,L ...

  7. java判断栈中元素数目_Java数据结构与算法-栈和队列

    (摘录加总结)------ 栈和队列不属于基础的数据结构,它们都属于线性表. 一.栈 对于栈存储操作元素只能在栈结构的一端进行元素的插入和删除,是一种性质上的线性表结构.按照"先进后出&qu ...

  8. 【数据结构与算法图文动画详解】终于可以彻底弄懂:红黑树、B-树、B+树、B*树、满二叉树、完全二叉树、平衡二叉树、二叉搜索树...

    1.树简介 1.1基本概念 树是由结点或顶点和边组成的(可能是非线性的)且不存在着任何环的一种数据结构.没有结点的树称为空(null或empty)树.一棵非空的树包括一个根结点,还(很可能)有多个附加 ...

  9. 数据结构和算法——栈、队列、堆

    文章目录 1.预备知识 1.1 栈 1.2 队列 1.3 堆 2.用队列实现栈 2.1 题目描述 2.2 解题思路 2.3 C++实现 3.用栈实现队列 3.1 题目描述 3.2 解题思路 3.3 C ...

  10. 经典数据结构和算法 双端队列 java

    选一个简单的数据结构聊一聊,话说有很多数据结构都在玩组合拳,比如说:块状链表,块状数组,当然还有本篇的双端队列,是的,它就是 栈和队列的组合体. 一:概念 我们知道普通队列是限制级的一端进,另一端出的 ...

最新文章

  1. Python:matplotlib实践:直方图、散点图展示、变色、线条变换、点样式变换、添加名称、设置横纵轴范围、在一张图上显示多条线
  2. .net framework 2.0 安装包下载url
  3. 基于matlab fdma传输系统设计,基于MATLAB的LTE系统仿真研究
  4. 各国自动驾驶政策概况及特征
  5. Nginx配置proxy_pass转发的/路径问题
  6. ML 01、机器学习概论
  7. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第39篇]侧信道攻击和故障攻击有什么区别
  8. mysql rac_MySQL Galera集群和ORACLE RAC的实现原理比较
  9. System.Windows.Forms.Application.DoEvents();
  10. 实习成长之路:后端开发实践系列——领域驱动设计(DDD)编码实践一
  11. wait放弃对象锁_Java线程:notify()和wait()示例
  12. kali免杀工具shellter
  13. 猪齿鱼开源四周年庆典,邀您共享精彩瞬间
  14. 操作系统 | 银行家算法
  15. openwrt路由器打印机服务器设置_openwrt路由器打印机服务器设置_TP-Link无线路由器打印机设置指南...
  16. excel设置斑马线
  17. 两个Listlt;Mapgt;快速合并
  18. git command
  19. 数据分析指标体系搭建实战!
  20. python中国最好大学排名_国内大学排名如何?用Python爬取中国大学排名

热门文章

  1. CP-ABE方案形式化定义及安全模型总结
  2. Web安全防范-----防止重放攻击
  3. ESP8266+micropython+HCSR04 超声波测距传感器
  4. conceptdraw office(专业办公套件) v6.0.0附安装教程
  5. 加js库和css库| jQuery hover()用法 |document.onreadystatechange |get和post
  6. JEECG3.3.0 配置大鱼短信
  7. wallpaper engine怎么用?
  8. 基于AKA的IMS接入认证机制
  9. java aes ctr_AES CBC和CTR加解密实例
  10. 用深度学习每次得到的结果都不一样,怎么办?