【问题标题】:Find number of leaves under each node of a tree查找树的每个节点下的叶子数
【发布时间】:2016-11-21 16:09:32
【问题描述】:

我有一棵树,用以下格式表示:

  • nodes 是树中节点的列表,从顶部开始按高度顺序排列。高度为 0 的节点是节点的第一个元素。高度为 1 的节点(从左到右读取)是节点的下一个元素,依此类推。

  • n_children 是一个整数列表,使得 n_children[i] = num 个节点的子节点[i]

例如给定一棵树,例如 {1: {2, 3:{4,5,2}}},nodes=[1,2,3,4,5,2], n_children = [2,0, 3,0,0,0]。

给定一棵树,只遍历一次树,是否可以生成节点和n_children以及节点中每个节点对应的叶子数?

这样的表示是独一无二的吗?或者两棵不同的树是否有可能具有相同的表示?

【问题讨论】:

  • is it possible for two different trees to have the same representation - 似乎并非如此:根据定义,表示定义了树,因此两个相同的表示构成同一棵树。

标签: algorithm tree


【解决方案1】:

对于第一个问题 - 创建给定树的表示:

我假设“给定的树”是指以节点对象的形式给出的树,每个都包含其值和对其子节点对象的引用列表。

我提出这个算法:

  1. node=root开始。
  2. 如果node.children 为空,则返回{values_list:[[node.value]], children_list:[[0]]}
  3. 否则:

    3.1。构造两个列表。一个将被称为values_list,每个元素都应有一个值列表。另一个将被称为children_list 并且每个元素都应有一个整数列表。这两个列表中的每个元素将代表子树中以node 开头的级别,包括node 本身(将在步骤3.3 中添加)。

    所以values_list[1] 将成为node 的子节点的值列表,values_list[2] 将成为node 的孙节点的值列表。 values_list[1][0] 将是node 的最左边子节点的值。而values_list[0] 将是一个只有一个元素的列表,values_list[0][0],这将是node 的值。

    3.2。对于node 的每个子节点(我们通过 node.children 引用):

    3.2.1。从 (2.) 重新开始,子节点设置为 node,返回的结果将相应地分配回(当函数返回时)child_values_listchild_children_list

    3.2.2。对于列表中的每个索引i(它们的长度相同),如果values_list[i] 中已经存在列表 - 将child_values_list[i] 连接到values_list[i] 并将child_children_list[i] 连接到children_list[i]。否则分配 values_list[i]=child_values_list[i]children_list[i]=child.children.list[i](这将是一个推送 - 添加到列表的末尾)。

    3.3。使node.value 成为新列表的唯一元素,并将该列表添加到values_list 的开头。使 node.children.length 成为新列表的唯一元素,并将该列表添加到 children_list 的开头。

    3.4。返回values_listchildren_list

  4. 当上面返回 values_listchildren_list for node=root(从步骤(1)开始)时,我们需要做的就是连接列表的元素(因为它们是列表,每个树的一个特定级别)。连接列表元素后,生成的 values_list_concatenatedchildren_list_concatenated 将是所需的表示形式。

在上面的算法中,我们仅通过将步骤 (2) 设置为 node 来访问一个节点,并且对于我们访问的节点的每个子节点,我们只执行一次。我们从根节点开始,每个节点只有一个父节点 => 每个节点都被访问一次。

对于与每个节点关联的叶子数:(如果我理解正确 - 子树中的叶子数一个节点是它的根),我们可以添加另一个列表,它将是生成并返回:leaves_list。 在停止情况下(node 没有孩子 - 步骤(2))我们将返回 leaves_list:[[1]]。在步骤 (3.2.2) 中,我们将像其他两个列表的列表元素一样连接列表元素。在步骤 (3.3) 中,我们将对第一个列表元素 leaves_list[0] 求和,并将该总和作为新列表中的唯一元素,我们将添加到 leaves_list 的开头。 (类似于leaves_list.add_to_eginning([leaves_list[0].sum()])


对于第二个问题 - 这种表示是否唯一:

为了证明唯一性,我们实际上想证明该函数(我们称其为 rep 表示“表示”)在树木空间上保持独特性。即它是injection。正如您在链接的 wiki 中看到的那样,这足以表明存在一个函数(我们称其为 tre 表示“树”)给定一个表示返回一棵树,并且对于每棵树 t 它都持有tre(rep(t))=t。简而言之 - 我们可以创建一个方法,该方法接受一个表示并从中构建一棵树,对于每棵树,如果我们制作它的表示并通过该方法传递该表示,我们将得到完全相同的树。

让我们开始吧!

实际上,第一项工作——创建该方法(函数tre)已经由你完成——按照你解释表示的方式。但是让我们明确一点:

  1. 如果列表为空,则返回空树。否则继续
  2. values[0] 作为其值和n_children[0] 作为其子节点数创建根节点(尚未创建子节点)。
  3. 启动列表索引i=1和级别索引li=1和级别元素索引lei=root.children.length和下一级元素累加器nle_acc=0
  4. lei>0: 4.1。对于lei 次: 4.1.1。创建一个以values[i] 为值,n_children[i] 为子节点数的节点。 4.1.2.将新节点添加为级别li 中尚未填充的最左侧子节点(从最左侧向右遍历树到li 级别,并将新节点分配给尚未分配的第一个引用。我们知道上一层已经完成,所以li-1 层中的每个节点都有一个children.length 属性,我们可以检查每个节点是否填充了他们应该拥有的子节点数) 4.1.3。添加nle_acc+=n_children[i] 4.1.4。递增++i 4.2.分配lei=nle_acc(级别元素可以获取累加器为它收集的内容) 4.3.清除nle_acc=0(下一级元素累加器需要从头开始累加下一轮)

现在我们需要证明通过第一个算法,然后通过第二个算法(这里是这个)的任意树将与原来一样摆脱所有这些。

由于我不是试图证明算法的正确性(尽管我应该这样做),所以我们假设它们按照我的意图去做。即第一个按照您的描述编写表示,第二个从左到右逐级创建树,从表示中分配一个值和子级的数量,并根据这些填充子级引用进入下一个级别时的数字。

因此,每个节点都根据表示具有正确数量的子节点(这就是子节点的填充方式),并且该数字是从树中写入的(在生成表示时)。值也是如此,因此它与原始树是同一棵树。

证明实际上应该更加详尽和详细 - 但我想我现在就这样吧。如果需要详细说明,也许我会将其作为实际证明。

【讨论】:

  • 感谢您的快速回复。但是这个算法给了我深度优先的元素。我需要它们处于广度优先级。
  • 我不太清楚你的意思。我知道您指的是第一个算法。它产生您指定的表示,特别是-您按级别获取节点-首先是根,然后是所有根的孩子,然后是根的孙子等等。你在哪里看到深度第一顺序?该算法首先进入深度(因为它使用递归),但这不是它返回的顺序。这就是在遍历树时为每个级别保留一个列表的全部意义所在。只有在遍历完成后,列表才会连接并返回。
  • 所以这里实际上需要考虑两件事。首先,要展平任意嵌套列表列表,需要再次遍历列表,对吗?还是有其他方法可以做到这一点?其次,给定像 {1:{2:{6,7},3:{4,5}}} 这样的树,我需要输出为 [1, 2, 3, 6, 7, 4, 5]。你的算法给了我 [1, 2, 6, 7, 3, 4, 5]。这就是我所说的深度优先。
  • 关于复杂性 - 你只指定你只想遍历 tree 一次,所以我之前没有多想。但是,是的,您可以在 O(1) 中连接 2 个列表,看起来 here。一旦你有一个 O(1) 连接,你只需要遍历列表列表,你就会得到 O(k),其中 k=number_of_lists=tree's_height。您在递归算法中也有串联(第一个算法中的步骤 (1) 到 (3)),但这不会是几秒钟的遍历 - 只是第一个遍历的一部分。
  • 关于正确性 - 我不确定你是如何得到这个结果的。只是为了确保我拿了你的例子并通过算法手动运行它并得到(如预期的那样) values_list_concatenated=[1,2,3,6,7,4,5], children_list_concatenated=[2,2,2,0, 0,0,0]。您是否记录了它有另一个 OS 不会显示的缩进的事实 - 步骤 (3.2) 是一个循环,步骤 (3.2.1) 和 (3.2.2) 是针对 node 的每个子节点执行的i>在继续步骤(3.3)之前?这意味着我们只有在为 node 的所有孩子完成 (3.2.1+2) 的表演后,才继续使用 node 执行 (3.3)?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-22
  • 1970-01-01
  • 1970-01-01
  • 2020-07-25
  • 1970-01-01
相关资源
最近更新 更多