选择包含K叶子的子树

时间:2012-11-18 12:06:46

标签: algorithm tree

我得到了一棵树T,其中有n个节点和l个树叶。

我必须选择一些包含k (<=l)叶子的子树。如果我选择节点t的祖先子树,我们就无法选择t的子树。

例如:

enter image description here

这是树T,它有13个节点(7个叶子)。

如果我想选择k = 4叶子,我可以选择节点4和6(或节点2和5)。这是选择的最小数量。 (我们也可以选择节点6,7,8,9,但这不是最小值。)

如果我想选择k = 5叶子,我可以选择节点3,这是选择的最小数量。

我想选择最小数量的子树。我只能找到O(nk^2)O(nk)算法,它使用BFS和动态编程。选择这个有更好的解决方案吗?

谢谢:)

1 个答案:

答案 0 :(得分:2)

实际上,要了解每个子树的叶子数量,您只需要通过每个节点一次,因此复杂度应为O(nm),其中m是平均值每个节点的子节点,在大多数情况下计算为O(n),因为m只是一个常量。要做到这一点,你应该:

  • 查找树的哪些节点是叶子
  • 向上移动树,为每个节点保存子树中的叶数

您可以通过从叶子开始并将父项放入队列来完成此操作。当您从队列中弹出节点n_i时,将每个子树中包含的叶子数从每个n_i的子节点开始求和。完成后,将n_i标记为已访问(因此您不会多次访问它,因为每个孩子可以添加一次)

这就是这样的:

^
|               f (3)              This node last
|              / \
|            /     \
|          /         \
|        /             \
|       d (2)           e (1)      These nodes second
|      /  \            /
|     /    \          / 
|    a (1)  b (1)    c (1)         These nodes first

步骤如下:

Find leaves `a`, `b` and `c`.
For each leave, add parent to queue   # queue q = (d, d, e)

Pop d                                 # queue q = (d, e)
Count leaves in subtree: d.leaves = a.leaves + b.leaves
Mark d as visited
Add parent to queue                   # queue q = (d, e, f)

Pop d                                 # queue q = (e, f)
d is visited, do nothing

Pop e                                 # queue q = (f)
Count leaves in subtree: e.leaves = c.leaves
Mark d as visited
Add parent to tree                    # queue q = (f, f)

Pop f                                 # queue q = (f)
Count leaves in subtree: f.leaves = d.leaves + e.leaves
Mark d as visited
Add parent to tree (none)

Pop f                                 # queue q = ()
f is visited, do nothing

您还可以使用智能数据结构来忽略添加两次的节点。请注意,您不能使用有序集,因为在“较高”节点之前探索“较低”节点非常重要。

在您的情况下,如果队列中的节点具有超过k个叶子,则可以消除队列中的节点,并返回找到的具有k个叶子的每个节点,这将提供更快的算法。