我正在努力写出一个分歧&征服树木的算法。对于除法步骤,我需要一种算法,通过删除节点,将给定的无向图G =(V,E)与n个节点和m个边分割成子树。所有子图应具有不包含 n / 2 节点的属性(树应尽可能分割)。首先,我尝试以递归方式从树中删除所有叶子以找到最后剩余的节点,然后我尝试在G中找到最长的路径并删除它的中间节点。下面给出的图表显示两种方法都不起作用:
是否有一些工作算法能够满足我的需求(在上述情况下返回节点H)。
答案 0 :(得分:2)
我认为你可以用这样的算法来做到这一点:
从根开始(如果树没有root,请选择任何节点)
在每个步骤中,尝试下降到具有最大子树的子节点(节点数“低于”它是最大的)。
如果这样做会使节点“高于”大于n / 2,则停止,否则继续该子节点。
如果树是合理平衡的,并且我们具有为每个节点预先计算的子树大小,则该算法应为O(log n)。如果其中一个条件不适用,则为O(n)。
答案 1 :(得分:2)
一个精确的算法就是这样,
从叶子开始并创建不相交的图形(实际上所有都是K1),在每个步骤中找到这个叶子的父级,并在节点x
具有{{1}的每个步骤中将它们合并到新树中已知子节点和节点度r
,j
,只是一个不在j = r+1
子节点中的节点是当前节点的父节点,在这种情况下我们说节点{{1} }}是x
,否则,有一些孩子没有构造相关的根子树,在这种情况下我们说节点x
是nice
。
因此,在每个步骤中将x
个节点连接到它们的相关父节点,显然每个步骤都需要bad
,每个步骤中至少有一个漂亮的节点(因为你从叶子开始),所以算法是O(n),它将完全完成,但是为了找到应该删除的节点,实际上每个步骤都需要检查一个dijoint列表(子树列表)的大小,这可以在O中完成( 1)在构造中,如果列表的大小等于或大于n / 2,则选择相关的nice节点。 (实际上在最小列表中找到满足此条件的nice节点。)
显而易见的是,如果可以以良好的方式划分树(每个部分最多有n / 2个节点)你可以通过这个算法完成它,但如果不是这样的话(实际上你不能将它分成两个或者更多的部分尺寸小于n / 2)这给你很好的近似。另外您可以看到输入树中没有假设。
注意:我不知道是否有树可以通过删除一个节点将其分割成一些小于n / 2的部分。
答案 2 :(得分:0)
此问题与查找对象的center of mass类似。 假设您的每个节点都是等质量(重量)的点质量,其位置由图中的位置给出。您的算法尝试查找质心,即在所有连接的子树中具有类似累积节点权重的节点。
您可以计算每个节点的所有子树的累计权重。然后选择最平衡的那个,s.t。没有子树的重量超过n/2
。可能这是一些动态编程的任务。
答案 3 :(得分:0)
这是我使用和测试过的一种方法。
首先,通过找到树的根,可以创建一个包含所有节点的集合,然后创建另一个数组NeighboursNumber [],其中每个节点的邻居数均存储了相应的索引。 然后遍历该集合并消除叶子(节点i具有NeighboursNumber [i] == 1),确保将这些节点添加到另一个集合RemovedSet中(以避免更新问题),然后在每次迭代之后遍历RemovedSet并减少集合中每个元素的所有邻居的NeighboursNumber []条目。 最后,您将拥有根节点。 (确保您实现的情况是剩下2个节点和1个邻居)。 找到根之后,我们继续找到每个子树的大小。这里的技巧是在寻找根的同时执行此操作:在第一次迭代中消除叶子之前,请先创建一个数组SubTreeSize [],然后每次从集合中删除一个节点时,都要将该节点的值+ 1到父级的值:SubTreeSize [parent] = SubTreeSize [parent] + SubTreeSize [removedNode] +1; 这样,当我们找到根时,我们还具有每个子树的大小。 然后,我们从根开始,检查每个邻居的子树大小+ 1>节点/ 2,如果是,则选择该节点并重新开始。当所有子节点的大小均为<= nodes / 2时,中断循环并输出该节点。
对于具有10 ^ 5个节点的树,此方法花费的时间不到一秒钟。