【问题标题】:How many possible combinations of numbers make the same binary tree有多少种可能的数字组合构成相同的二叉树
【发布时间】:2015-04-09 08:12:08
【问题描述】:

设置:

我有一个代表二叉树的数字列表。第一个数字的处理方式与其他数字不同,它是根。在“其余”的数字中,有些会高于根,有些会低于根。较大的数字被排序到左边,而较小的数字被排序到右边。示例:

list = [5,7,6,8,9,2,1,3,4]
root = 5
higher = [7,6,8,9] #in order of appearance
    root = 7
    higher = [8,9]
    lower  = [6]
lower  = [2,1,3,4] #in order of appearance
    root = 2
    higher = [3,4]
    lower  = [1]

在这种情况下,树看起来像这样:

                                      5
                        -------------| |--------------
                       |                             |
                       7                             2
             8--------| |-------6             3-----| |-----1
         ---|                              ---|
        9                                 4

我正在寻找一种方法来模拟列表[5,7,6,8,9,2,1,3,4] 可以排列的可能组合的数量,以便生成相同的二叉树。解决方案肯定是递归的,在“更高”和“更低”数字列表中,它们可以被分解得更多。

可以通过分解树来开始计算所有数字的排列方式,就像我们对上面的列表所做的那样。

父母可以混,孩子可以混,但是孩子和父母不能混

higher = [7,6,8,9]

higher 列表不需要保留[7,6,8,9] 的顺序。只有根较高且不是另一棵树的父项的项目才需要按外观顺序保留。由于68 都是7 的孩子,它们可以互换,但9 必须在8 之前,因为它是它的孩子。所以基本上重新排列这个列表的唯一规则是:

  • 必须以 7 开头
  • 8 必须始终在 9 之前

因此与此有三种组合。

[7,6,8,9]
[7,8,6,9]
[7,8,9,6]

所有这些都可以分解成相同的子树,所以我们的条件满足了,现在我们可以查看元素列表lower而不是主根。

lower = [2,1,3,4]

下层列表也不需要保留它的顺序,它遵循类似的规则,可以用三种不同的方式来生成相同的树:

[2,1,3,4]
[2,3,1,4]
[2,3,4,1]

我们现在有以下信息: - 较低的可以写3种方式 - 较高的可以写3种方式

它们可以组合多少种不同的方式来生成同一棵树?这是 3^3 吗?还是更多?

看看我知道的数字:

list = [5,7,6,8,2,1,3,4]

如果我列出每个位置可能出现的数字,这就是我最终能走多远:

列表的第一个元素必须是 5,它是根。之后必须是27,因为其他任何东西都会破坏较高/较低列表的顺序。之后就乱七八糟了。

如果第二个数字 = 2,则第三个数字可以是 1、3 或 7 这三个数字之一。

如果第二个数字 = 7,则第三个数字可以是 6、8 或 2 这三个数字之一。

在此之后,它扩展得更大,并且组合上升得非常快。我的问题是,有没有办法以有效的方式递归检索总可能组合的数量?我将在 python 中执行此操作。谢谢。

【问题讨论】:

    标签: python algorithm recursion binary-tree combinations


    【解决方案1】:

    以上述想法为基础....这里是生成所有等效排序的 python 生成器代码。

    import collections
    
    Node = collections.namedtuple('Node', ('root','left', 'right'))
    
    def tree_from_list(lst):
        if not lst:
            return None
        root = lst[0]
        left_lst = [x for x in lst if x > root]
        right_lst = [x for x in lst if x < root]
        return Node(root,tree_from_list(left_lst), tree_from_list(right_lst))
    
    def parent(key, tree):
        if tree is None:
            return -1
        elif (tree.left != None) and (tree.left.root == key): 
            return tree.root
        elif (tree.right != None) and  (tree.right.root == key):
            return tree.root
        elif (key > tree.root):
            return parent(key, tree.left)
        else: return parent(key, tree.right)
    
    def all_insert_after(key, target, seq):
        i = seq.index(key)
        for j in range(i,len(seq)):
                mylist =  seq[:j+1] + [target] + seq[j+1:]
                yield mylist
    
    def all_equivalent_orderings(seq):
        if (len(seq)==1):
        yield seq
        else:
            z = seq[-1]
            p = parent(z, tree_from_list(seq))
            for a in all_equivalent_orderings(seq[:-1]):    
                for b in all_insert_after(p,z,a):
                    yield b  
    
    print "Here are all 630 equivalent orderings of [5,7,6,8,9,2,1,3,4]"         
    for o in all_equivalent_orderings([5,7,6,8,9,2,1,3,4]):
        print o
    

    【讨论】:

      【解决方案2】:

      据我了解,您希望拓扑顺序的数量与每个节点与其父节点都有弧的图形兼容。我将在未经测试的 Python 中绘制一个解决方案。首先我们需要一个节点数据类型。

      import collections
      
      Node = collections.namedtuple('Node', ('left', 'right'))
      

      一棵树要么是None(空树),要么是Node,其中两个字段都是树。

      现在,空树的拓扑序数为1,即只是空序。非空树的拓扑阶数由以下计数参数给出。根源永远是第一位的。在根之后,左子树的任意拓扑顺序与右子树的任意拓扑顺序任意打乱。因此公式是三个因素的乘积。

      import math
      
      def num_shuffles(left_size, right_size):  # binomial coefficient
          return (math.factorial(left_size + right_size) //
                  (math.factorial(left_size) * math.factorial(right_size)))
      
      def num_orders_and_size(tree):
          assert isinstance(tree, (type(None), Node))
          if tree is None:
              return (1, 0)
          else:
              left_num_orders, left_size = num_orders_and_size(tree.left)
              right_num_orders, right_size = num_orders_and_size(tree.right)
              return (num_shuffles(left_size, right_size) *
                      left_num_orders * right_num_orders,
                      left_size + 1 + right_size)
      

      为了从列表中构建树,我们使用递归(有更快的方法,但这个最简单)。

      def tree_from_list(lst):
          if not lst:
              return None
          root = lst[0]
          left_lst = [x for x in lst if x > root]
          right_lst = [x for x in lst if x < root]
          return Node(tree_from_list(left_lst), tree_from_list(right_lst))
      

      【讨论】:

      • 为了让它工作,我是否需要遍历 python 列表并使每个元素成为具有子节点的节点,从而将列表变成树?我想我不完全确定您在解决方案的开头是如何进行的。
      • @DomFarolino 是的,我们需要构建树。很快就会有更多代码。
      • 你的解决方案太棒了,我真的很喜欢。我正在尝试学习很多关于数学密集型算法的知识,并且非常感谢您可以通过这个简短地解释您的思维过程。我想看看我是否走在了正确的轨道上。我真的很想知道你是如何以这种方式解决这个问题的。非常感谢。
      • @DomFarolino 找出问题的简明数学描述(计算特定类别的偏序的线性扩展)花费了大部分时间。我很担心,因为这个问题对于一般偏序来说是#P-hard,但后来我注意到你的类是串并联的子类,它有一个很好的算法,我适应了。我不能肯定地说我是按原样记住算法还是使用我对枚举组合的流畅性快速重建它。
      • 太棒了,非常感谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-09
      相关资源
      最近更新 更多