Background

在堆的合并中,仅仅只是拷贝都需要 的时间复杂度,所以希望提出一种更好的数据结构,让堆的合并的效率更高

Definition

外节点

只有一个儿子或没有儿子的节点,即左右儿子至少有一个为空节点的节点。

距离/零路长

距离/零路径长 null path length 定义为 X 结点到它的后代中离它最近的外结点的最短路径长度,即两结点之间路径的权值和。特别的,外结点的距离为 0 ,空结点的距离为

左式堆

左偏树/左式堆:一种满足左偏性质的堆有序的二叉树,其左偏性质体现在左儿子的距离大于等于右儿子的距离。即对于任意一个节点,

左式堆的距离

左偏树/左式堆的距离:我们将一棵左偏树根结点的距离作为该树的距离。

合并操作

  • 如果两个堆中有一个堆为空,就直接返回另一个堆。
  • 否则为了合并两个堆,需要比较它们的根。
    • 将具有大的根值的堆H2与具有小的根值的堆H1的右子堆H3合并得到一个新的左式堆,其中H2和H3的合并也采用同样的逻辑。

合并代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void LeftistHeap::merge(LeftistHeap &rhs) {
if (this == &rhs) return;
root = merge(root, rhs.root);
rhs.root = nullptr;
}

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);
}
LeftistNode *merge1(LeftistNode *h1, LeftistNode *h2) {
if (h1->left == nullptr)
h1->left = h2;
else {
h1->right = merge(h1->right, h2);
if (h1->left->dist < h1->right->dist)
swapChildren(h1);
h1->dist = h1->right->dist + 1;
}
return h1;
}

插入操作

左式堆的插入就是新建一个只有一个节点的左式堆再和原来的合并

1
2
3
void insert(const Comparable &x) {
root = merge(new LeftistNode(x), root);
}

删除操作

删除的本质就是删除根节点,得到左右两个子树的两个堆再做合并

1
2
3
4
5
6
7
8
9
10
11
void deleteMin() {
if (empty()) throw underflow_error("LeftistHeap Empty");
LeftistNode *oldRoot = root;
root = merge(root->left, root->right);
delete oldRoot;
}

void deleteMin(Comparable &minItem) {
minItem = findMin();
deleteMin();
}

时间复杂度分析

以上三个操作的复杂度都依赖于合并操作,其复杂度均为

相关文章

[[Heap]]