如何找到一组树木,每一棵树都跨越另一棵树?

时间:2013-09-23 14:26:23

标签: algorithm search tree

想象一下,它给出了一组树 ST ,并且每棵树的每个顶点都被标记。还给出了另一个树 T (也有标签顶点)。问题是如何从 T 的根开始找到 ST 的哪些树可以跨越树 T 以这种方式生成树 T'的顶点标签与 T 顶点的标签重合。请注意, T 的每个顶点的子项应完全覆盖或根本不覆盖 - 不允许部分覆盖儿童。换言之:给定一个树并执行以下过程:选择一个顶点并删除此顶点下方的所有顶点和边(顶点本身除外)。找到 ST 的树,以便通过应用于 T 的一系列过程生成每棵树。 例如,给定树 T

enter image description here

树木

enter image description here enter image description here

enter image description here enter image description here

覆盖T和树

enter image description here

不是因为这棵树有孩子3,5不像T那样有2,3个孩子。我能想到的最好的事情要么是暴力破坏它,要么找到一组树,其中每个树都有与T相同的根标签,然后在这些树中搜索答案,但我想这两个树都不是方法是最佳方法。我想的是以某种方式对树木进行了扫描,但没有任何结果。有什么想法吗?

注意:

  • 树不一定是二元的
  • T 可以覆盖另一棵树 T'如果他们共享根
  • 树被订购意味着你不能交换任何两个孩子的位置。

TL; DR 找到一个有效的算法,在查询时使用给定的树 T 算法查找来自给定(固定/静态)集 ST 的所有树,它们能够覆盖的Ť

2 个答案:

答案 0 :(得分:1)

我将草拟答案,然后提供一些有效的源代码。

首先,你需要一个algorithm to hash a tree。在不失一般性的情况下,我们可以假设每个树节点的子节点从最小到最大排序(反之亦然)。

ST 的每个成员上运行此算法并保存哈希值。

现在,将您的测试树 T 生成并生成保留原始根的所有子树 TP 。您可以通过以下方式(可能效率低下)执行此操作:

  1. 设置 S 的节点
  2. 生成 S
  3. 的功率集 P
  4. 通过从 T
  5. 的副本中删除 P 的每个成员中存在的节点来生成子树
  6. 将保留原始根目录的子树添加到 TP
  7. 现在生成一组 TP 的所有哈希值。

    现在检查 ST 哈希中的每一个 TP 的成员身份。

    ST 哈希存储需要 ST 中的O(n)空格,并且可能需要空间来容纳树。

    您可以优化会员代码,使其不需要存储空间(我的测试代码中没有这样做)。该代码将需要大约2次 N次检查,其中 N 是** T 中的节点数。

    因此算法在O(H 2**N)中运行,其中 H 的大小为 ST N 是指的节点数的Ť即可。 加快这一过程的最佳方法是找到一种改进的算法来生成 T 的子树。

    以下Python代码可以实现此目的:

    #!/usr/bin/python
    import itertools
    import treelib
    import Crypto.Hash.SHA
    import copy
    
    #Generate a hash of a tree by recursively hashing children
    def HashTree(tree):
      digester=Crypto.Hash.SHA.new()
    
      digester.update(str(tree.get_node(tree.root).tag))
    
      children=tree.get_node(tree.root).fpointer
      children.sort(key=lambda x: tree.get_node(x).tag, cmp=lambda x,y:x-y)
    
      hash=False
      if children:
        for child in children:
          digester.update(HashTree(tree.subtree(child)))
        hash = "1"+digester.hexdigest()
      else:
        hash = "0"+digester.hexdigest()
    
      return hash
    
    #Generate a power set of a set
    def powerset(iterable):
      "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
      s = list(iterable)
      return itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s)+1))
    
    #Generate all the subsets of a tree which still share the original root
    #by using a power set of all the tree's nodes to remove nodes from the tree
    def TreePowerSet(tree):
      nodes=[x.identifier for x in tree.nodes.values()]
      ret=[]
      for s in powerset(nodes):
        culled_tree=copy.deepcopy(tree)
        for n in s:
          try:
            culled_tree.remove_node(n)
          except:
            pass
        if len([x.identifier for x in culled_tree.nodes.values()])>0:
          ret.append(culled_tree)
    
      return ret
    
    def main():
      ST=[]
    
      #Generate a member of ST
      treeA = treelib.Tree()
      treeA.create_node(1,1)
      treeA.create_node(2,2,parent=1)
      treeA.create_node(3,3,parent=1)
      ST.append(treeA)
    
      #Generate a member of ST
      treeB = treelib.Tree()
      treeB.create_node(1,1)
      treeB.create_node(2,2,parent=1)
      treeB.create_node(3,3,parent=1)
      treeB.create_node(4,4,parent=2)
      treeB.create_node(5,5,parent=2)
      ST.append(treeB)
    
      #Generate hashes for members of ST
      hashes=[(HashTree(tree), tree) for tree in ST]
    
      print hashes
    
      #Generate a test tree
      T=treelib.Tree()
      T.create_node(1,1)
      T.create_node(2,2,parent=1)
      T.create_node(3,3,parent=1)
      T.create_node(4,4,parent=2)
      T.create_node(5,5,parent=2)
      T.create_node(6,6,parent=3)
      T.create_node(7,7,parent=3)
    
      #Generate all the subtrees of this tree which still retain the original root
      Tsets=TreePowerSet(T)
    
      #Hash all of the subtrees
      Thashes=set([HashTree(x) for x in Tsets])
    
      #For each member of ST, check to see if that member is present in the test
      #tree
      for hash in hashes:
        if hash[0] in Thashes:
          print [x for x in hash[1].expand_tree()]
    
    main()
    

答案 1 :(得分:0)

要验证一棵树是否覆盖另一棵树,必须至少查看一棵树的所有顶点。通过一次查看第一棵树的所有顶点来验证树覆盖另一棵树是微不足道的。因此,如果只需要检查一棵树,那么最简单的算法就已经是最优的。

以下所有内容都是我未经考验的未经考验的成果。

如果有许多可能的T必须针对相同的ST进行检查,则可以将ST的树存储为这些事实的集合

root = 1
children of node 1 = (2, 3)
children of node 2 = ()
children of node 3 = ()

这些事实可以存储在两个表中的标准关系数据库中,“根”(字段“tree”和rootnode“)和”branches“(字段”tree“,”node“和”children“)。可以构建SQL查询或一系列查询以快速查找匹配的树。我的SQL-fu是基本的,所以我无法在单个查询中管理它,但我相信它应该是可能的。