【问题标题】:How to count children in a tree如何数一棵树上的孩子
【发布时间】:2013-04-27 18:27:33
【问题描述】:

我有一个数字列表:

[1, 2, 3, 4, 5, 6, 7]

如果列表中有一棵树,我有兴趣找到一种算法,该算法可以汇总此列表中的所有子项:

                              1
                            /   \
                           2     3
                          / \   / \
                         4   5 6   7

我正在寻找一种算法:

[6, 2, 2, 0, 0, 0, 0]


A = 6
B = 2
C = 2
D = 0
E = 0
F = 0
G = 0

每个节点(除了叶子)都有两个孩子。唯一的例外是如果列表是偶数:

                              1
                            /   \
                           2     3
                          / \   / 
                         4   5 6   

我想避免构建一棵树,然后计算每个节点的子节点数。必须有一种简单的数学方法来计算列表中的孩子的数量?

【问题讨论】:

  • 为什么树看起来和你的例子一样?具体来说,为什么 5 不是 2 而不是 6 的儿子?
  • 如何将数组转换为树?在您的示例中,您从根开始,然后是 l(eft) 节点,十个 r(ight) 节点,然后是 ll,然后是 rl,然后是 lr,然后是 rr,接下来是什么? lll,rll,lrl,rrl,llr,rlr,lrr,rrr?基本上首先是下一代的所有左节点,然后是下一代的右节点?
  • 谢谢。我在原始树中有一个错误。
  • @Gal 我假设他将任意树根植于任意节点,并且子树结构由 BFS 诱导。只有在数据结构中,树才被认为是有根有序的,通常情况并非如此。
  • @turtle 您可以在构建/更改树时跟踪所需的信息,只需使用反映该信息的计数器增加节点,并在更改时将该更改传播到根。这将花费与需要更改计数器的插入/删除/旋转/(无论更改树的结构)操作相同的复杂性。

标签: algorithm


【解决方案1】:

1-indexed 数组。

那么对于索引为i的节点,左子索引为2*i,右子索引为2*i+1。

然后从头到尾遍历数组,对于现在的节点:

如果他(左或右)儿子的索引超出数组范围,则他没有(左或右)儿子。

如果不是,那么你可以知道他儿子的孩子的数量(我们从那个数组开始遍历)。结果=现在儿子的孩子数量+现在儿子的数量。

例如:

[1, 2, 3, 4, 5, 6, 7]
A is the result array.
1.A=[0, 0, 0, 0, 0, 0, 0],now(now is a index) = 7(1-indexed) since 7*2>7, a[7]=0
2.A=[0, 0, 0, 0, 0, 0, 0],now = 6,since 6*2>7, a[6]=0
3.A=[0, 0, 0, 0, 0, 0, 0],now = 5,since 5*2>7, a[5]=0
4.A=[0, 0, 0, 0, 0, 0, 0],now = 4,since 4*2>7, a[4]=0
5.A=[0, 0, 2, 0, 0, 0, 0],now = 3,since 3*2<7 and 3*2+1<7, a[3]=2+a[6]+a[7]=2
6.A=[0, 2, 2, 0, 0, 0, 0],now = 2,since 2*2<7 and 2*2+1<7, a[2]=2+a[4]+a[5]=2
7.A=[6, 2, 2, 0, 0, 0, 0],now = 1,since 1*2<7 and 1*2+1<7, a[1]=2+a[2]+a[3]=6

【讨论】:

  • 这不是真的 - 参见示例(2 的儿子是 4 和 6,而不是 4 和 5)。
【解决方案2】:

对于树平衡的情况(即输入列表中的元素个数是奇数),可以这样计算:

n = length of elements in input list

那么对于输出列表中的元素i

d = depth of element in tree = floor(log2(i+1))+1

那么树中那个元素下面的子元素的数量是:

n_children = n - ((2^d)-1) / 2^(d-1)

因此,对于您的 [1,2,3,4,5,6,7] 示例:

n = 7

对于数组位置0(即节点1):

d = depth = floor(log2(1))+1 = 1
n_children = (7 - ((2^1)-1)) / 2^(1-1)
           = (7 - 1) / 2^0
           = 6 / 1
           = 6

那么对于then数组位置1,(即节点2):

d = depth = floor(log2(2))+1 = 2
n_children = (7 - ((2^2)-1)) / 2^(2-1)
           = (7 - 3) / 2
           = 2

继续这样做,对于 i=0 到 i=6,将得到 [6, 2, 2, 0, 0, 0, 0]。

Python 代码如下所示:

import math

def n_children(list_size, i):
    depth = math.floor(math.log(i+1,2)) + 1
    return (list_size - ((2**depth)-1)) / 2**(depth-1)

print [n_children(7, i) for i in range(7)]

这会输出[6.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0]

不过,它需要一些修改来处理偶数输入数组(最简单的方法可能是将数组大小四舍五入到最接近的奇数,然后从 i 或类似的任何奇数值中减去 1)。

【讨论】:

    【解决方案3】:

    将第一个数组解释为堆,其中节点 n 的子节点在 2*n+1 和 2*n+2,然后递归遍历树:

    def children(t, n):
        if 2 * n + 1 >= t:
            return 0
        elif 2 * n + 2 >= t:
            return 1
        else:
            return 2 + children(t, 2 * n + 1) + children(t, 2 * n + 2)
    
    size = 7
    childcounts = [ children(size, i) for i in range(size) ]
    print(childcounts)
    

    这将打印:

    [6, 2, 2, 0, 0, 0, 0]

    【讨论】:

    • 当然,列表的后半部分永远是零,你真的只需要...for i in range(size // 2)
    【解决方案4】:

    就像我们在堆中做的那样, children[i] = 其所有孩子的孩子的总和 + 孩子的数量

    与第 0 个元素一样,a[0] = 其左孩子的孩子数量 + 其右孩子的孩子数量 + 其孩子的数量 所以 a[0] = 2 + 2 + 2

    for(int i=n-1;i>=0;i--) {
    if(i*2+2 < n) a[i]+=a[i*2+2]+1;
    if(i*2+1 < n) a[i]+=a[i*2+1]+1;
    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-20
      • 2010-10-15
      • 2011-07-27
      • 1970-01-01
      相关资源
      最近更新 更多