如何合并满足堆顺序的两棵树?

Posted

技术标签:

【中文标题】如何合并满足堆顺序的两棵树?【英文标题】:How can I merge two trees which satisfy the heap order? 【发布时间】:2021-12-26 01:19:18 【问题描述】:

是否可以在时间 O(m+n+1) 内合并两棵满足堆顺序的树?而 m 和 n 是输入树的高度。

Example 

Input:
   10              8
     \
      9 
Output: (Can be any one of them)
   10               10             10          10
     \             /  \           /  \        /  
      9           9    8         8    9      9
     /                                      /
    8                                      8

【问题讨论】:

请注意,并非所有输入/输出树都是堆。我想这是故意的。 是的,树不都是堆,但值满足堆顺序属性 【参考方案1】:

是的,这可以在 O(? + ?) 内完成,条件是允许输出树与输入树共享节点。

算法

给定两个根节点ab

    如果ab 都为null,则返回null(基本情况) 如果其中一个为空,则选择另一个。否则选择具有较大值的节点。假设a 是该选定节点。 使用与a 相同的值创建一个新节点x。 执行递归合并a.leftb。将合并结果附加到x.left 将未更改的a.right 引用分配给x.right 返回x

由于我们在递归的每一级都降低了主题中的一棵树的高度,因此递归深度最多为两棵输入树的高度之和,由此遵循给定的时间复杂度。

在步骤 3 中合并 a.lefta.right 的选择是任意的。你可以随意设置。

示例实现

这是一个粗略的 javascript 实现。当你运行这个 sn-p 时,会合并以下两棵树:

        10              8
      /   \            / \
     4     9          7   6
    / \   / \              \
   3   1 2   5              0

class Node 
    constructor(value, left=null, right=null) 
        this.value = value;
        this.left = left;
        this.right = right;
    
    
    toString() 
        return (this.right ? this.right.toString().replace(/^/gm, "  ") + "\n" : "")
             + this.value
             + (this.left ? "\n" + this.left.toString().replace(/^/gm, "  ") : ""); 
    


function merge(a, b) 
    if (a == null && b == null) return null;
    if (a == null || (b != null && b.value > a.value))  
        return new Node(b.value, merge(a, b.left), b.right);
    
    return new Node(a.value, a.left, merge(b, a.right));


// Example
let a = new Node(10, 
    new Node(4, 
        new Node(3), 
        new Node(1)
    ), 
    new Node(9, 
        new Node(2), 
        new Node(5)
    )
);

let b = new Node(8, 
    new Node(7), 
    new Node(6, 
        null, 
        new Node(0)
    )
);

console.log("a:");
console.log(a.toString());
console.log("b:");
console.log(b.toString());
console.log("merged:");
console.log(merge(a, b).toString());

这个 sn-p 有一个非常基本的打印功能——它打印旋转 90° 且根在左侧的树。没有连接节点的线(你必须想象它们),只是缩进。

该示例将生成此树:

        10
      /   \
     4     9
    / \   / \
   3   1 8   5
        / \
       7   6
          / \
         2   0

注意:您提到了 O(? + ? + 1),但那个附加常数无关紧要:O(? + ? + 1) = O(? + ?)。

【讨论】:

这看起来是一个很好的解决方案,但请注意,OP 将 m 和 n 定义为输入树的*高度,而不是它们的节点数大小,我认为这就是为什么它们正在询问 O(n+m) 是否可能(因为 O(N+M) 与 N 和 M 是节点数是微不足道的 - 只需将两棵树中的所有节点放在一个数组中并构建一个新堆)。但无论如何,你展示了他们的要求! 哦,是的,错过了!相应地更新了我对 ? 和 ? 的使用。

以上是关于如何合并满足堆顺序的两棵树?的主要内容,如果未能解决你的问题,请参考以下文章

第八篇

关于石子合并

CSU1811: Tree Intersection

[CTSC2018]暴力写挂——边分树合并

石子合并

BZOJ4919[Lydsy六月月赛]大根堆 线段树合并