查找树的每个节点下的叶子数

时间:2016-07-18 22:46:44

标签: algorithm tree

我有一棵树,其格式如下:

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

  • n_children是一个整数列表,这样n_children [i] =节点的子节点[i]

例如,给定像{1:{2,3:{4,5,2}}}这样的树,节点= [1,2,3,4,5,2],n_children = [2,0, 3,0,0,0]。

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

这种表现形式是独一无二的吗?或者两个不同的树可能具有相同的表示形式吗?

1 个答案:

答案 0 :(得分:2)

对于第一个问题 - 在给定树的情况下创建表示法:

我假设一个特定的树"我们指的是以节点对象的形式给出的树,每个树都保存其值以及对其子节点对象的引用列表。

我提出这个算法:

  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返回node=root时(来自步骤(1)),我们需要做的就是连接列表的元素(因为它们是列表,每个列表都有一个特定级别的树)。连接list-elements后,生成的values_list_concatenatedchildren_list_concatenated将成为想要的代表。

  5. 在上面的算法中,我们只通过开始步骤(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为#34;树")给出一个表示给出了一个树返回,并且每个树都有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. while 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(下一级元素累加器需要从下一轮开始累积)
    5. 现在我们需要证明通过第一个算法然后通过第二个算法(这里的这个算法)传递的任意树将完全不同于原来的那个。

      由于我并未尝试证明算法的核心性(尽管我应该这样做),但我们假设它们按照我的意图行事。也就是说,第一个按照你的描述写出表示,第二个按照从左到右的顺序创建一个树,从表示中分配一个值和子数,并根据这些来填充子参考。当涉及到下一个级别的数字。

      因此,每个节点根据表示(孩子们如何填充)具有适当数量的子节点,并且该数字是从树中写入的(生成表示时)。对于值也是如此,因此它与原始树是相同的树。

      证据实际上应该更详细和详细 - 但我想我现在就把它留在那里。如果需要详细说明,我可以将其作为实际证明。