Bagaking细讲算法 堆 III: 堆的合并

本文详细介绍了堆的合并问题,特别是针对Binary heap在合并时的困难。文章探讨了Skew Heap和Leftist Heap两种优化合并的堆结构,包括它们的合并复杂度和优化策略。Skew Heap通过特殊的合并方式控制高度,而Leftist Heap利用s-value控制节点高度,确保合并效率。最后,文章总结了不同类型的堆,并预告了下篇文章将介绍堆在优先队列中的应用。
摘要由CSDN通过智能技术生成

堆 III: 堆的合并

上篇我们聊过了 heap 的 operations, 其中有 merge 这个操作. 这样的堆实现, 就叫做 mergeable heap.

Binary heap 合并时的困难

Binary heap 在合并时没有太好的办法, 最好的方法也只是直接以 O ( n ) O(n) O(n) 复杂度 Heapify.

稍微想想, 会发现问题出在 implicit 的实现上. 堆本身是一个 l g n lgn lgn 层结构, 并且有非常良好的性质父节点必然(大于/小于)子节点的特性, 在不用保证堆是一棵近似完全的二叉树的情况下, 显然合并是非常简单的:

以小根堆为例, 两个小根堆的 root 分别为 p, q, 已知 p < q, 我们的目标是把 p 和 q 合并成一个新堆 R. 显然有一种暴力的方法是, 直接令 R 为 p, 只要在 R 中找到一个适合 q 的位置, 可以直接将 q 节点插入 R. 由于堆的性质, 其每一棵子树合法的充分必要条件是该子树也是一个堆, 因此只需要直接把整个 q 堆插入到对应位置, 即合并完成. 寻找 q 的位置需要 O ( l g n ) O(lgn) O(lgn) 时间, 因此整个时间复杂度为 O ( l g n ) O(lgn) O(lgn).

这样做打破了 binary heap “is essentially an almost complete tree” 的要求, 所以这个结构:

  1. 无法再称为 binary heap, 即使也是2叉树实现
  2. 一般情况下不再用 implicit 实现

当然, 这只是一个思路. 这个思路提示我们, 可以通过设计其他的堆, 来做到易于合并.

Skew Heap

延续刚才的思路, 因为不是完全树, 合并后的高度不再是最好情况, 这无疑会导致实际计算次数的增加, 以及一个更差的最坏情况, 比如插入和弹出的最坏复杂度降为 O ( n ) O(n) O(n).

那我们要用什么方法, 在保证合并复杂度为 O ( l g n ) O(lgn) O(lgn) 的同时, 尽量控制它的高度增长呢. 有一个明显的思路是, 不吧 Q 堆简单暴力的插入 P 堆, 而是考虑 Q 堆的子树, 使其尽可能均匀的合并到 P 堆. 由于我们考虑的是子树, 而不是每个元素, 所以这个算法应该还是 lgn 的.

斜堆 Skew Heap, 由 Sleator 和 Tarjan 提出, 正是这样一个方法, 其 merging 过程为:

  1. 比较两个最小堆, p表示小的那个, q表示大的那个
  2. 令所求堆 R 的 root 为 p 的 root p r o o t p_{root} proot, 使 p 的左子节点成为 R 的右子节点 R r = p l R_r=p_l Rr=pl
  3. 接下来, 递归以上操作, 将 q 和 p r p_r pr 合并到 R 的左子树 R l R_l Rl, 直到全部合并
const compareHeapNode = (h1, h2) => h1.v < h2.v ? [ h1, h2 ] : [ h2, h1] ;
function merge(h1, h2) {
   
    if(!h1 || !h2) return h1 || h2;
    let [{
   l: pl, r: pr}, q] = compareHeapNode(h1, h2);
    return {
    r: pl, l: merge(pr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值