给出了两个BSTs
(二进制搜索树)。如何在给定的两个binary trees
中找到最大的公共子树?
编辑1: 这就是我的想法:
设,r1 =第一棵树的当前节点 r2 =第二棵树的当前节点
There are some of the cases I think we need to consider:
Case 1 : r1.data < r2.data
2 subproblems to solve:
first, check r1 and r2.left
second, check r1.right and r2
Case 2 : r1.data > r2.data
2 subproblems to solve:
- first, check r1.left and r2
- second, check r1 and r2.right
Case 3 : r1.data == r2.data
Again, 2 cases to consider here:
(a) current node is part of largest common BST
compute common subtree size rooted at r1 and r2
(b)current node is NOT part of largest common BST
2 subproblems to solve:
first, solve r1.left and r2.left
second, solve r1.right and r2.right
我可以想一想我们需要检查的案例,但截至目前我无法对其进行编码。这不是一个家庭作业问题。它看起来像吗?
答案 0 :(得分:5)
只需散列每个节点的子节点和键,然后查找重复项。这将给出线性预期时间算法。例如,请参阅以下伪代码,它假定没有哈希冲突(处理冲突会很简单):
ret = -1
// T is a tree node, H is a hash set, and first is a boolean flag
hashTree(T, H, first):
if (T is null):
return 0 // leaf case
h = hash(hashTree(T.left, H, first), hashTree(T.right, H, first), T.key)
if (first):
// store hashes of T1's nodes in the set H
H.insert(h)
else:
// check for hashes of T2's nodes in the set H containing T1's nodes
if H.contains(h):
ret = max(ret, size(T)) // size is recursive and memoized to get O(n) total time
return h
H = {}
hashTree(T1, H, true)
hashTree(T2, H, false)
return ret
请注意,这是假定BST子树的标准定义,即子树由节点及其所有后代组成。
答案 1 :(得分:3)
假设树中没有重复值:
LargestSubtree(Tree tree1, Tree tree2)
Int bestMatch := 0
Int bestMatchCount := 0
For each Node n in tree1 //should iterate breadth-first
//possible optimization: we can skip every node that is part of each subtree we find
Node n2 := BinarySearch(tree2(n.value))
Int matchCount := CountMatches(n, n2)
If (matchCount > bestMatchCount)
bestMatch := n.value
bestMatchCount := matchCount
End
End
Return ExtractSubtree(BinarySearch(tree1(bestMatch)), BinarySearch(tree2(bestMatch)))
End
CountMatches(Node n1, Node n2)
If (!n1 || !n2 || n1.value != n2.value)
Return 0
End
Return 1 + CountMatches(n1.left, n2.left) + CountMatches(n1.right, n2.right)
End
ExtractSubtree(Node n1, Node n2)
If (!n1 || !n2 || n1.value != n2.value)
Return nil
End
Node result := New Node(n1.value)
result.left := ExtractSubtree(n1.left, n2.left)
result.right := ExtractSubtree(n1.right, n2.right)
Return result
End
简要解释一下,这是解决问题的一种蛮力解决方案。它做了第一棵树的广度优先步行。对于每个节点,它执行第二个树的BinarySearch
以定位该树中的相应节点。然后使用这些节点,它评估根植于那里的公共子树的总大小。如果子树大于以前找到的任何子树,则会在以后记住它,以便在算法完成时构造并返回最大子树的副本。
此算法不处理重复值。可以通过使用BinarySearch
实现来扩展它,该实现返回具有给定值的所有节点的列表,而不是仅返回单个节点。然后算法可以迭代此列表并评估每个节点的子树,然后照常进行。
此算法的运行时间为O(n log m)
(它遍历第一个树中的n
个节点,并为每个节点执行log m
二进制搜索操作),使其与标准值相同最常见的排序算法。运行时空间复杂度为O(1)
(除了几个临时变量之外没有分配),而O(n)
返回结果时(因为它创建了子树的显式副本,可能不需要,具体取决于究竟该算法应如何表达其结果)。因此,即使这种强力方法也应该表现得相当好,尽管正如其他答案所指出的那样,O(n)
解决方案是可行的。
还可以对此算法应用可能的优化,例如跳过先前评估的子树中包含的任何节点。因为树行走是广度优先的,所以我们知道,作为某个先前子树的一部分的任何节点都不能成为更大子树的根。在某些情况下,这可以显着提高算法的性能,但最坏情况下的运行时间(没有共同子树的两棵树)仍然是O(n log m)
。
答案 2 :(得分:1)
答案 3 :(得分:1)
以下算法计算两个二叉树的所有最大公共子树(不假设它是二叉搜索树)。设S和T为两个二叉树。该算法从树的底部起作用,从树叶开始。我们首先确定具有相同值的叶子。然后考虑他们的父母并识别具有相同孩子的节点。更一般地,在每次迭代时,我们识别节点,只要它们具有相同的值并且它们的子节点是同构的(或者在交换左右儿童之后是同构的)。该算法终止于T和S中所有最大子树对的集合。
以下是更详细的说明:
让S和T成为两个二叉树。为简单起见,我们可以假设对于每个节点n,左子节点具有值&lt; =右子节点。如果节点n中只有一个子节点为NULL,则假定右节点为NULL。 (一般来说,我们认为两个子树是同构的,如果它们取决于每个节点的左/右孩子的排列。)
(1)查找每棵树中的所有叶节点。
(2)定义一个二分图B,其边缘从S中的节点到T中的节点,最初没有边。设R(S)和T(S)为空集。设R(S)_next和R(T)_next也是空集。
(3)对于S中的每个叶节点和T中的每个叶节点,如果节点具有相同的值,则在B中创建边。对于从S中的nodeS到T中的nodeT创建的每个边,将nodeS的所有父节点添加到集合R(S),并将nodeT的所有父节点添加到集合R(T)。
(4)对于R(S)中的每个节点nodeS和T(S)中的每个节点nodeT,如果它们具有相同的值,则在B中绘制它们之间的边缘 { (i):nodeS-&gt; left连接到nodeT-&gt; left和nodeS-&gt; right连接到nodeT-&gt; right,OR (ii):nodeS-&gt; left连接到nodeT-&gt; right和nodeS-&gt; right连接到nodeT-&gt; left,OR (iii):nodeS-&gt; left连接到nodeT-&gt; right和nodeS-&gt; right == NULL和nodeT-&gt; right == NULL
(5)对于步骤(4)中创建的每条边,将其父节点添加到R(S)_next和R(T)_next。
(6)如果(R(S)_next)是非空的{
(i)交换R(S)和R(S)_next并交换R(T)和R(T)_next。
(ii)清空R(S)_next和R(T)_next的内容
(iii)返回步骤(4)。
}
当此算法终止时,R(S)和T(S)包含S和T中所有最大子树的根。此外,二分图B标识S中的所有节点对和T中的节点,它们给出同构子树。
我相信这个算法的复杂度是O(n log n),其中n是S和T中节点的总数,因为集合R(S)和T(S)可以按值排序存储在BST中但是我有兴趣看到证据。