树的分而治之算法

Posted

技术标签:

【中文标题】树的分而治之算法【英文标题】:Divide-And-Conquer Algorithm for Trees 【发布时间】:2012-01-01 21:00:35 【问题描述】:

我正在尝试为树编写分治算法。对于除法步骤,我需要一种算法,通过删除一个节点,将具有n 个节点和m 个边的给定无向图G=(V,E) 划分为子树。所有子图都应具有不包含超过 n/2 个节点的属性(树应尽可能等分)。首先,我尝试从树中递归删除所有叶子以找到最后一个剩余节点,然后我尝试找到 G 中最长的路径并删除它的中间节点。下面给出的图表表明这两种方法都不起作用:

是否有一些工作算法可以满足我的要求(在上述情况下返回节点 H)。

【问题讨论】:

我不明白,如果你删除 H 你会得到 9 个子树! 是的,很抱歉在这里不清楚,我可以得到很多子树,但我不希望一个大于图表的一半,以确保我只对除步进行对数计数。 还有一件事,你如何把“树应该尽可能相等地分割”变成一个可计算的值? G 中没有被移除节点的连接组件的最大尺寸应该被最小化。 一个算法仍然被认为是 O(logN),即使它在每一步都没有将树完全减半。将其减半有什么重要性? 【参考方案1】:

我认为你可以用这样的算法来做到这一点:

从根开始(如果树没有根,选择任何节点)。 在每一步中,尝试下降到具有最大子树的子节点(“下面”的节点数最大)。 如果这样做会使“上方”的节点数大于 n/2,则停止,否则继续该子节点。

如果树是合理平衡的并且我们为每个节点预先计算了子树的大小,则该算法应该是 O(log n)。如果其中一个条件不适用,则为 O(n)。

【讨论】:

什么是无向树的根?另外我怎么知道子树有多大? 就像我说的,如果你没有给定根,你可以选择任何节点作为根。并且要知道子树有多大,您必须计算它,理想情况下缓存结果,这样您就不必多次计算它。 这肯定超过 O(n),假设您从示例中的节点 A 开始。您将首先扫描整个子树,然后移至 B,然后移至 C,依此类推,每次扫描整个子树时都会得到更高的时间。 你不需要扫描整个子树,你只需要知道计数。您可以在第一次需要时在 O(n) 中计算所有这些。 现在我明白了,我接受了,因为它似乎更容易实现。【参考方案2】:

一个精确的算法是这样的,

从叶子开始并创建不相交的图(实际上都是K1),在每一步中找到这个叶子的父节点,并将它们合并到新树中,在每个步骤中如果节点xr已知子节点并且节点的度数是j,这样j = r+1,只是一个不在x的子节点中的节点是当前节点的父节点,在这种情况下我们说节点xnice,否则,有一些child 使得它们的相关根子树没有被构造,在这种情况下我们说节点xbad

所以在每一步中将nice 节点连接到它们相关的父节点,很明显每一步都需要sum of degree of parent nice nodes 在每一步中你至少有一个不错的节点(因为你从叶子开始),所以算法是 O (n),它会完全完成,但是为了找到应该删除的节点,实际上在每个步骤中都需要检查双联列表(子树列表)的大小,这可以在 O(1) 中完成, 另外如果列表的大小等于或大于n/2,则选择相关的nice节点。 (实际上是在满足这个条件的最小列表中找到好的节点)。

显而易见的是,如果可以以良好的方式划分树(每个部分最多有 n/2 个节点),您可以通过此算法完成,但如果不是这样(实际上您不能将其一分为二或尺寸小于 n/2 的更多部分)这为您提供了很好的近似值。同样如您所见,输入树中没有任何假设。

注意:我不知道是否有可能拥有一棵树,从而无法通过删除一个节点将其划分为小于 n/2 的某些部分。

【讨论】:

【参考方案3】:

这个问题似乎类似于寻找对象的center of mass。 假设您的每个节点都是等质量(重量)的点质量,并且其位置由图中的位置给出。您的算法试图找到质心,即在所有连接的子树中具有相似的节点累积权重的节点。

您可以计算每个节点的所有子树的累积权重。然后选择最平衡的一个,s.t.没有子树的重量超过n/2。可能这是一些动态编程的任务。

【讨论】:

【参考方案4】:

这是我使用并测试过的一种方法。

从查找树的根开始,您可以通过创建一个包含其中所有节点的集合,然后创建另一个数组 NeighboursNumber[],其中每个节点的邻居数存储相应的索引。 然后遍历集合并消除叶子(节点 i 具有 NeighboursNumber[i] == 1 ),确保将这些节点添加到另一个集合 RemovedSet (以避免更新问题),然后在每次迭代后通过 RemovedSet 并减少集合中每个元素的所有邻居的 NeighboursNumber[] 条目。 最后,您将拥有根节点。 (确保您实现的情况是剩下 2 个节点和 1 个邻居)。 在找到根之后,我们继续找到每个子树的大小。这里的技巧是在寻找根的同时执行此操作:在第一次迭代中消除叶子之前,首先创建一个数组 SubTreeSize[] ,每次我们从集合中删除一个节点时,我们添加该节点的值 + 1 到父节点的值: SubTreeSize[parent] = SubTreeSize[parent] + SubTreeSize[removedNode] + 1 ; 这样,当我们找到根时,我们也有每个子树的大小。 然后我们从根开始检查每个邻居的子树大小 + 1 > nodes / 2 ,如果是,那么我们选择那个节点并重新开始。当所有子节点大小为

对于具有 10^5 个节点的树,此方法花费的时间不到一秒。

【讨论】:

以上是关于树的分而治之算法的主要内容,如果未能解决你的问题,请参考以下文章

决策树

C4.5算法学习

分而治之的冒泡排序算法

将分而治之的递归算法转换为迭代版本

为啥二分搜索是一种分而治之的算法?

一个“分而治之”的算法分配