我有一个深度为 L 级的树数据结构,每个节点大约有N 个节点。我想计算出树中节点的总数。要做到这一点(我认为),我需要知道将有孩子的节点的百分比。
N 中叶节点与非叶节点的比例的正确术语是什么?
计算三个节点总数的公式是什么?
更新有人在其中一个答案中提到了分支因素,但随后消失了。我想这是我一直在寻找的术语。那么公式不应该考虑分支因素吗?
更新我应该对假设的数据结构进行估计,而不是确切的数字!
我有一个深度为 L 级的树数据结构,每个节点大约有N 个节点。我想计算出树中节点的总数。要做到这一点(我认为),我需要知道将有孩子的节点的百分比。
N 中叶节点与非叶节点的比例的正确术语是什么?
计算三个节点总数的公式是什么?
更新有人在其中一个答案中提到了分支因素,但随后消失了。我想这是我一直在寻找的术语。那么公式不应该考虑分支因素吗?
更新我应该对假设的数据结构进行估计,而不是确切的数字!
好的,每个节点都有大约 N 个子节点,树的深度为 L 级。
With 1 level, the tree has 1 node.
With 2 levels, the tree has 1 + N nodes.
With 3 levels, the tree has 1 + N + N^2 nodes.
With L levels, the tree has 1 + N + N^2 + ... + N^(L-1) nodes.
节点总数为 (N^L-1) / (N-1)。
好的,只是一个小例子,为什么它是指数的:
[NODE]
|
/|\
/ | \
/ | \
/ | \
[NODE] [NODE] [NODE]
|
/|\
/ | \
只是为了纠正第一个答案中的错字:深度为 L 的树的节点总数为 (N^(L+1)-1) / (N-1)...(即幂 L +1 而不仅仅是 L)。
这可以如下所示。首先,我们的定理:
1 + N^1 + N^2 + ... + N^L = (N^(L+1)-1)/(N-1)
两边乘以 (N-1):
(N-1)(1 + N^1 + N^2 + ... + N^L) = N^(L+1)-1。
展开左侧:
N^1 + N^2 + N^3 + ... + N^(L+1) - 1 - N^1 - N^2 - ... - N^L。
所有项 N^1 到 N^L 都被取消,剩下 N^(L+1) - 1。这是我们的右手边,所以初始等式是正确的。
深度L的节点数计算公式为:(假设有N个根节点)
NL _
要计算所有节点的数量,需要对每一层执行此操作:
for depth in (1..L)
nodeCount += N ** depth
如果只有 1 个根节点,则从 L 中减去 1 并将总节点数加 1。
请注意,如果在一个节点中的叶子数量与平均情况不同,这可能会对您的数量产生很大影响。树的位置越高,影响越大。
* * * N ** 1 *** *** *** N ** 2 *** *** *** *** *** *** *** *** *** N ** 3
这是社区 wiki,所以请随意更改我令人震惊的代数。
如果您的树大约是完整的,即除了最后两个之外,每个级别都有其完整的子节点,那么您的叶子节点介于 N^(L-2) 和 N^(L-1) 之间,并且介于 N^(L -1) 和 N^L 个节点总数。
如果你的树没有满,那么知道叶节点的数量并没有帮助,因为一棵完全不平衡的树将有一个叶节点,但有任意多个父节点。
我想知道你的陈述“每个节点大约有 N 个节点”有多精确——如果你知道平均分支因子,也许你可以计算出树的预期大小。
如果您能够找到叶子与内部节点的比率,并且您知道平均子节点数,则可以将其近似为 (n*ratio)^N = n。这不会给你答案,但我想知道数学比我更好的人是否能想出一种方法将 L 插入这个方程并给你一些可解的东西。
尽管如此,如果您想准确地知道,您必须遍历树的结构并随时计算节点。
Knuth 的估计器 [1],[2] 是一个点估计,它以任意有限树中的节点数为目标,无需遍历所有节点,即使树不平衡。Knuth 的估计器是无偏估计器的一个例子;Knuth 估计器的期望值将是树中的节点数。话虽如此,如果所讨论的树不平衡,Knuth 的估计量可能会有很大的差异,但在你的情况下,由于每个节点都会有大约 N 个子节点,我认为 Knuth 的估计量的方差应该不会太大。当人们试图测量执行蛮力搜索所需的时间时,这个估计器特别有用。
对于以下函数,我们将假设所有树都表示为列表的列表。例如,[]
表示具有单个节点的树,[[],[[],[]]]
将表示具有 5 个节点和 3 个叶子的树(树中的节点与左括号一一对应)。以下函数是用 GAP 语言编写的。
该函数simpleestimate
给出了树中节点数的估计值tree
。背后的想法simpleestimate
是我们随机选择从树的根 x_0 到叶子 x_n 的路径 x_0,x_1,...,x_n。假设 x_i 有 a_i 个后继者。然后simpleestimate
将返回 1+a_1+a_1*a_2+...+a_1*a_2*...*a_n。
point:=tree; prod:=1; count:=1; list:=[];
while Length(point)>0 do prod:=prod*Length(point); count:=count+prod; point:=Random(point); od;
return count; end;
该函数estimate
将简单地给出通过simpleestimate(tree)
samplesize
多次应用该函数给出的估计值的算术平均值。
estimate:=function(samplesize,tree) local count,i;
count:=0;
for i in [1..samplesize] do count:=count+simpleestimate(tree); od;
return Float(count/samplesize); end;
示例: simpleestimate([[[],[[],[]]],[[[],[]],[]]]);
在返回15
时
estimate(10000,[[[],[[],[]]],[[[],[]],[]]]);
返回10.9608
(树实际上确实有 11 个节点)。
如果您只知道树的深度,那么您计算总大小的唯一选择就是遍历并计算它们。