左式堆
- 左式堆同样是一种堆结构满足堆序性质。左式堆是一种趋向不平衡的二叉树。
- 零路径长(Null Path Length, NPL):节点X到一个没有两个孩子的最短路径长。
- 具有0个或1个孩子的节点的NPL为0,空节点NPL为-1。
- 任一节点的NPL比它孩子的NPL的最小值多1。且其左孩子的NPL大于或等于右孩子的NPL,这使得树偏向左增加深度。
- 在右路径上有r个节点的左式树必然至少有 2r−1 2 r − 1 个节点。
左式堆的基本结构
- 采用指针构造,每个节点为一个树节点包含:关键字值,左孩子域,右孩子域以及额外的零路径长信息。
typedef struct Node { // 定义左式堆的结点结构
ElemType data; // 节点中的数据
struct Node* left; // 左孩子
struct Node* right; // 右孩子
int Npl; // 节点的零路径长
}TreeNode, *LeftHeap;
左式堆的合并操作
- 合并是左式堆的基本操作,采用递归的方式进行。
- 首先将具有大的根值的堆1与具有小的根植堆2的右子树合并成堆3。
- 将合并好的堆3与堆2合并(堆3成为堆2的右孩子)成堆4。整个过程递归进行,且保证满足堆序性。
- 堆4的右子树比左子树深,最后一步将堆4的左右子树互换得到堆5,并更新NPL。
合并操作的驱动函数
LeftHeap Merge(LeftHeap LH1, LeftHeap LH2) // 合并左式堆的驱动函数
{
if (LH1 == nullptr)
return LH2;
if (LH2 == nullptr)
return LH1;
if (LH1->data < LH2->data)
return Merge1(LH1, LH2);
else
return Merge1(LH2, LH1);
}
合并操作的实现函数
LeftHeap Merge1(LeftHeap LH1, LeftHeap LH2)
{
if (LH1->left == nullptr) // 单节点,左孩子为空而Npl=0保证了右孩子也为空
{
LH1->left = LH2; // 直接将LH2赋给LH1的左孩子
}
else
{ // LH1不是单节点,
LH1->right = Merge(LH1->right, LH2); // 则,先让LH1的右孩子与LH2合并
if (LH1->left->Npl < LH1->right->Npl) // 合并完之后,如果LH1左孩子的零路径长小于右孩子的零路径长,不满足左式堆的条件
{ // 交换左右孩子
LeftHeap temp = LH1->left;
LH1->left = LH1->right;
LH1->right = temp;
}
LH1->Npl = LH1->right->Npl + 1; // 更新LH1的零路径值
}
return LH1;
}
左式堆的插入操作
- 插入是合并的特殊情况,即合并左式堆与一个单节点。
void insert(LeftHeap &LH, ElemType x)
{
TreeNode* p = new TreeNode; // 申请一个单节点用于储存x的值
p->data = x;
p->left = p->right = nullptr;
p->Npl = 0;
LH = Merge(LH, p); // 合并LH与p,这里把单节点看成一个树
}
左式堆的删除操作
- 删除操作同样基于合并操作,删除根节点,合并左右子树。
ElemType DeleteMin(LeftHeap &LH) // 删除优先队列最小值
{
LeftHeap Left = LH->left; // 把左式堆分成两个左右两个子左式堆
LeftHeap Right = LH->right;
ElemType x = LH->data;
delete LH; // 释放根节点
LH = Merge(Left, Right); // 合并左右两个子堆
return x; // 返回删除的最小值
}
附左式堆实现及相关操作C/C++
#include<iostream>
using namespace std;
typedef int ElemType;
typedef struct Node { // 定义左式堆的结点结构
ElemType data; // 节点中的数据
struct Node* left; // 左孩子
struct Node* right; // 右孩子
int Npl; // 节点的零路径长
}TreeNode, *LeftHeap;
LeftHeap Merge(LeftHeap LH1, LeftHeap LH2);
LeftHeap Merge1(LeftHeap LH1, LeftHeap LH2);
bool isEmpty(LeftHeap LH) // 判断堆是否为空
{
return LH == nullptr;
}
LeftHeap Merge(LeftHeap LH1, LeftHeap LH2) // 合并左式堆的驱动函数
{
if (LH1 == nullptr)
return LH2;
if (LH2 == nullptr)
return LH1;
if (LH1->data < LH2->data)
return Merge1(LH1, LH2);
else
return Merge1(LH2, LH1);
}
LeftHeap Merge1(LeftHeap LH1, LeftHeap LH2)
{
if (LH1->left == nullptr) // 单节点,左孩子为空而Npl=0保证了右孩子也为空
{
LH1->left = LH2; // 直接将LH2赋给LH1的左孩子
}
else
{ // LH1不是单节点,
LH1->right = Merge(LH1->right, LH2); // 则,先让LH1的右孩子与LH2合并
if (LH1->left->Npl < LH1->right->Npl) // 合并完之后,如果LH1左孩子的零路径长小于右孩子的零路径长,不满足左式堆的条件
{ // 交换左右孩子
LeftHeap temp = LH1->left;
LH1->left = LH1->right;
LH1->right = temp;
}
LH1->Npl = LH1->right->Npl + 1; // 更新LH1的零路径值
}
return LH1;
}
void insert(LeftHeap &LH, ElemType x)
{
TreeNode* p = new TreeNode; // 申请一个单节点用于储存x的值
p->data = x;
p->left = p->right = nullptr;
p->Npl = 0;
LH = Merge(LH, p); // 合并LH与p,这里把单节点看成一个树
}
ElemType DeleteMin(LeftHeap &LH) // 删除优先队列最小值
{
LeftHeap Left = LH->left; // 把左式堆分成两个左右两个子左式堆
LeftHeap Right = LH->right;
ElemType x = LH->data;
delete LH; // 释放根节点
LH = Merge(Left, Right); // 合并左右两个子堆
return x; // 返回删除的最小值
}
int main()
{
const int rawdata[] = { 19, 13, 9, 8, 23, 39, 4, 2, 75, 100, 43, 58 };
int tablesize = 12;
LeftHeap myLeftHeap = nullptr; // 初始化左式堆为空指针
for (int i = 0;i < sizeof(rawdata) / sizeof(int);i++)
{
insert(myLeftHeap, rawdata[i]); // 向堆中插入给定数据
}
cout << "Prior Deque from the Heap : \n"; // 依次优先出队列
while (!isEmpty(myLeftHeap))
{
cout << DeleteMin(myLeftHeap) << " ";
}
cout << endl;
delete myLeftHeap;
system("pause");
return 0;
}
- 操作实现结果
Prior Deque from the Heap :
2 4 8 9 13 19 23 39 43 58 75 100
参考资料
Mark Allen Weiss: 数据结构与算法分析