左式堆是在二叉堆的基础上出现的,主要是为了解决二叉堆合并的效率。
二叉堆合并H1与H2的合并,依此将H2中的根节点删除并插入H1节点中,从而完成合并。
左式堆的结构
1.左式堆具有堆序性质,左式堆不是完全二叉树,是趋于左倾的不平衡二叉树。
左式堆的性质
1.把任一节点X的零路径长Npl(X)定义为X到一个没有两个儿子的节点的最短路径长,即有一个子节点或零个子节点的节点X的Npl值为0。
2.对于堆中的每一个节点X,左儿子的零路径长,至少与右儿子的零路径长一样。(这个性质确保了树不平衡的要求,更偏重于使树向左增加深度。)
左式堆的操作
1.合并
2.插入,插入是合并的一种特殊情况,把插入的节点看作一个单结点的左式堆与一个大的左式堆合并
3.删除,将左式堆的根节点删除,得到两个子树,将两个子树合并即可
合并的递归实现思路
1.如果两个堆由一个堆是空的,就返回另外一个堆。
2.如果两个堆都不为空,则比较两个堆根值的大小,将根值大的堆与根值小的堆的右子树进行合并,直到合并完成
左式堆的数据结构
每个节点除了包括属性值,左指针,右指针,还包括一个指示零路径长的项。
左式堆总是在右边做堆合并,保持左边不变,左式的合并的效率取决于右路径的节点长度。左式堆的合并效率为 O(log N)
代码
头文件即数据结构
#include<stdio.h>
#include<stdlib.h>
struct TreeNode;
typedef struct TreeNode *PriorityQueue;
//节点的数据结构
struct TreeNode {
int Element; //属性值
PriorityQueue Left; //左指针
PriorityQueue Right; //右指针
int Npl; //零路径长
};
//函数的声明
PriorityQueue Initialize();
int FindMin(PriorityQueue H);
int IsEmpty(PriorityQueue H);
PriorityQueue Merge(PriorityQueue H1,PriorityQueue H2);
#define Insert(X,H) (H = Insert1((X),H))
#define DeleteMin(H) (H = DeleteMin1(H))
PriorityQueue Insert1(int X, PriorityQueue H);
PriorityQueue DeleteMin1(PriorityQueue H);
static PriorityQueue Merge1(PriorityQueue H1,PriorityQueue H2);
函数的实现
PriorityQueue Initialize(void) { //左式堆初始化
return NULL;
}
int IsEmpty(PriorityQueue H) { //判断堆是否为空
if (H == NULL) {
return 1;
}
return 0;
}
int FindMin(PriorityQueue H) { //找到最小的值,即树根位置
if (!IsEmpty(H)) {
return H->Element;
}
return NULL;
}
插入及删除函数
PriorityQueue Insert1(int X, PriorityQueue H) { //插入堆
//创建一个单节点的堆,与大的堆进行合并操作
PriorityQueue NewHeap = (PriorityQueue)malloc(sizeof(struct TreeNode));
NewHeap->Element = X;
NewHeap->Left = NULL;
NewHeap->Right = NULL;
NewHeap->Npl = 0;
H = Merge(H, NewHeap);
return H;
}
PriorityQueue DeleteMin1(PriorityQueue H) { //删除根节点,调整堆
//将堆的根节点删除,将左右子树合并为一个左式堆。
if (IsEmpty(H)) {
printf("堆为空!\n");
return H;
}
PriorityQueue LeftHeap, RightHeap;
LeftHeap = H->Left;
RightHeap = H->Right;
free(H); //释放根节点
return Merge(LeftHeap, RightHeap); //合并左右子树
}
合并函数
PriorityQueue Merge(PriorityQueue H1, PriorityQueue H2) {
//合并操作,判断两个堆是否为空,并比较两个堆根节点的大小,将根节点大的与根节点小的合并
if (H1 == NULL) {
return H2;
}
if (H2 == NULL) {
return H1;
}
if (H1->Element<H2->Element) {
return Merge1(H1, H2);
}
return Merge1(H2, H1);
}
static PriorityQueue Merge1(PriorityQueue H1,PriorityQueue H2) {
//将H2与H1的右子树合并
if (H1->Left == NULL) {
H1->Left = H2; //H1是一个单节点,H1的右子树已为空,H1的零路径长为0
}
else {
H1->Right = Merge(H1->Right, H2); //将H2与H1的右子树合并
if (H1->Left->Npl < H1->Right->Npl) {
//如果右子树的Npl 大于 左子树的 Npl,两个子树进行交换即可保持左式堆的性质
PriorityQueue T;
T = H1->Left;
H1->Left = H1->Right;
H1->Right = T;
}
H1->Npl = H1->Right->Npl + 1; //更新零路径长度,为右子树零路径长+1
}
return H1;
}