堆在数组中是隐式存储的,在时间和空间上都具有极大的优势。但是若要进行两个堆的合并操作,数组描述的堆就难已完成,于是引入链式描述的左高树。左高树分为高度优先的左高树HBLT和重量优先的左高树WBLT,本文只涉及HBLT。
对于一棵二叉树,引入概念外部节点,它表示树中的所有空子树。引入函数s(x),它表示根节点x到达其子节点的外部节点的最短距离。由该定义可知,如果根节点x的左子树或右子树为空,说明x直接与外部节点相连,则s(x)=1;而若s(x)=k,则说明x之下的(k-1)层都是不存在任何外部节点,即都被内部节点所填满。
HBLT要求对于树中的所有节点都满足其左子树的s()大于等于其右子树的s()。而最大HBLT还要求树满足大根数的性质,即根节点的数据值大于等于所有子节点的数据值。
最大HBLT最重要的函数是合并,其插入和删除操作都可以转化为两棵最大HBLT的合并。合并利用递归来完成,首先比较两树根节点的大小,将根节点小的树并入到根节点大的树,此时转化为将根节点小的树与根节点大的树的右子树进行合并,从而产生递归。合并结束之后需要自下而上维护整棵树,使之保持最大HBLT的性质,即如果某节点的左子树的s()小于右子树的s(),则需要交换左右子树,然后重新计算该节点的s()。
插入则相当于一个最大HBLT与一个只有一个节点的最大HBLT进行合并;删除则相当于把当前树的根节点的左右子树进行合并。
代码
#include<iostream>
#include<queue>
using namespace std;
template<class T>
struct binaryTreeNode//定义二叉树节点
{
T element;//当前节点数值
binaryTreeNode<T>* leftChild;//左子节点指针
binaryTreeNode<T>* rightChild;//右子节点指针
binaryTreeNode()
{
leftChild = NULL;
rightChild = NULL;
}
binaryTreeNode(const T& theElement)
{
element = theElement;
leftChild = NULL;
rightChild = NULL;
}
binaryTreeNode(const T& theElement, binaryTreeNode<T>* LEFT, binaryTreeNode<T>* RIGHT)
{
element = theElement;
leftChild = LEFT;
rightChild = RIGHT;
}
};
template<class T>
class maxHBLT//最大HBLT类
{
private:
void meld(binaryTreeNode<pair<int, T>>*& x, binaryTreeNode<pair<int, T>>*& y)//递归合并两个左高树,合并结果为x,此函数需要封装
{
//递归终点
if (y == nullptr)
return;
if (x == nullptr)
{
x = y;
return;
}
//保证x根节点所存数据值比y大
if (x->element.second < y->element.second)
swap(x, y);
//x与y的合并转化为x的右子树与y的合并,从而进行递归
meld(x->rightChild, y);
//递归结束之后需要对左高树的形状以及各个节点的s()进行维护
if (x->leftChild == nullptr)//x的左子树为空,则交换左右子树
{
x->leftChild = x->rightChild;
x->rightChild = nullptr;
x->element.first = 1;
}
else
{
if (x->leftChild->element.first < x->rightChild->element.first)//x的左子树的s()小于右子树的s(),两者交换,并重新计算x的s()
swap(x->leftChild, x->rightChild);
x->element.first = x->rightChild->element.first + 1;//左高树根节点的s()就是从根节点一直向右走到达外部节点的距离
}
}
binaryTreeNode<pair<int, T>>* root;//根节点,其元素为一个数对,前项表示根节点的s(),后项表示根节点存储的数据
int treeSize;//节点总个数
public:
maxHBLT()//空树
{
root = nullptr;
treeSize = 0;
}
T front()//获取最大值(根节点的数据值)
{
return root->element.second;
}
//插入:相当于原树与一个只有一个元素的最大HBLT树合并
void push(const T& theElement)
{
binaryTreeNode<pair<int, T>>* newNode = new binaryTreeNode<pair<int, T>>(pair<int, T>(1, theElement));
meld(root, newNode);
treeSize++;
}
//删除:相当于根节点的左右子树合并
void pop()
{
if (treeSize == 0)
{
cout << "树为空" << endl;
return;
}
binaryTreeNode<pair<int, T>>* left = root->leftChild;
binaryTreeNode<pair<int, T>>* right = root->rightChild;
delete root;
root = left;
meld(root, right);
treeSize--;
}
//合并两个最大HBLT树
void meld(maxHBLT<T>& theHBLT)
{
meld(root, theHBLT.root);
treeSize += theHBLT.treeSize;
theHBLT.root = nullptr;
theHBLT.treeSize = 0;
}
//将一个数组转化为最大HBLT树
void initialize(T* theElements,int theSize)
{
if (theSize < 1)
{
cout << "初始化无效" << endl;
return;
}
queue<binaryTreeNode<pair<int, T>>*> queue;
delete root;
for (int i = 1; i <= theSize; i++)//先将数组中的元素转化为孤立的树的节点,存入队列中
queue.push(new binaryTreeNode<pair<int, T>>(pair<int, T>(1, theElements[i])));
for (int i = 1; i <= theSize - 1; i++)//然后循环取出两个节点,合并,然后再存入队列中
{
binaryTreeNode<pair<int, T>>* node1 = queue.front();
queue.pop();
binaryTreeNode<pair<int, T>>* node2 = queue.front();
queue.pop();
meld(node1, node2);
queue.push(node1);
}
root = queue.front();//最终所有节点合并成一个最大HBLT树
treeSize = theSize;
}
//输出树(检查用)
void print()
{
int size = treeSize;
for (int i = 0; i < size; i++)
{
cout << front() << " ";
pop();
}
cout << endl;
}
};