从列表重建树,深度信息封装在列表的条目中

时间:2013-11-23 13:50:21

标签: algorithm data-structures tree binary-search-tree

我们通过depth first traversal从树(必然是二元搜索树)构建了一个列表。

里面的每个条目都是一对(k, d),k是节点的关键,d是该节点的深度。

现在我们需要从列表中构建原始树。

我们怎么做?


请注意

  1. 树不一定是二叉搜索树
  2. 我们不知道深度优先遍历是pre-orderin-order还是post-order

  3. 我的问题是

    • 我们能否在条件下实现这种逆向工程?我知道对于二叉搜索树,我们需要至少两个遍历列表(例如,顺序和后序列表)来重建原始树。
      • 如何?如果可能的话

3 个答案:

答案 0 :(得分:1)

需要注意的事项:

  • 有序遍历会生成一个唯一的树
  • 预订和下订单不会:

    你无法区分这两者:

      1    1
     /      \
    2        2
    

    我只会在左边生成一个(这样做会让事情变得容易多了。)

我们现在可以说什么:

  • 如果第一个节点是根(即不是深度0):

    我们要么按顺序执行空左子树,要么预先订购。

  • 如果最后一个节点是root:

    我们要么使用空的右子树或按顺序进行有序的操作。

  • 如果以上都不是:

    我们正在进行有序遍历。

对于上面我们不知道要进行哪些遍历的两种情况,最简单的方法是尝试为两种可能的遍历生成树,并丢弃哪一种不起作用(基于以下限制),如果是。

一些限制:

对于有序,如果当前节点为空,我们就无法向右或向上。

对于预订,如果当前节点为空,我们不能向左或向右。

对于后期订单,我们必须在设置当前节点后上升 - 我们不能在没有设置当前节点的情况下向左或向右移动。

在所有情况下,我们都会在上行之前向左走。

通过'向左'或'向右',我的意思是创建一个(空的)左或右孩子并遍历到该节点。
通过'上升',我的意思是简单地在已经创建的树中向上遍历。

基于上述限制,应该很容易编写算法来生成树。作为有序的例子:

  1. 如果新节点的深度比当前节点的深度深:
    1. 如果当前节点为空并且没有左子节点,我们可以创建一个左子节点并将其设置为当前节点
    2. 否则,如果当前节点不为空并且没有正确的子节点,我们可以创建一个正确的子节点并将其设置为当前节点
  2. 否则,如果深度与当前节点相同且当前节点为空,则为 将该节点的值设置为新节点
  3. 如果上述情况均未触发且当前节点为空,则 将当前节点的父节点设置为当前节点
  4. 如果上述情况均未触发,则失败
  5. 如果触发1.1,1.2或3,则从1开始重复。
  6. 示例:

    输入:(f, 2), (g, 2), (b, 1), (i, 2), (c, 1), (a, 0)

    由于(a, 0)是根,我们无论是按顺序还是后订购。

    然后我们生成2个子树:

    in-order        post-order
        .                .
       /                /
      .                .
     /                /
    f                f
    

    .表示空节点)

    当我们获得(g, 2)时,我们已经可以丢弃有序树,因为我们无法从f的父级向右或向上,因为它是空的,所以我们是卡住。

    然后我们继续下订单:

        .
       /
      .
     / \
    f   g
    
    
        .
       /
      b
     / \
    f   g
    
    
         .
       /   \
      b     .
     / \   /
    f   g i
    
    
         .
       /   \
      b     c
     / \   /
    f   g i
    
    
         a
       /   \
      b     c
     / \   /
    f   g i
    

答案 1 :(得分:0)

我不确定你的前/后/有序是什么意思,如果你知道第一次访问每个节点的时间,那么带有深度数据的单个DFS运行应该允许你重建树(我猜那会相当于您定义的“预购”。在非二叉树中甚至没有很好地定义有序(父级是否会出现在第一个节点之后?在第二个节点之后?如果只有一个孩子会怎样?)

如果您可以告诉发现每个节点的顺序,您可以在深度增加时查看列表,创建越来越多的子节点(并跟踪每个深度遇到的 last 节点),一旦你获得了不增加的深度,你就可以确定你需要多少级别才能放置下一个节点。
具有相同深度的两个列表连续节点将是同一父节点的兄弟节点,并且通常,如果最后一个节点具有深度d1并且您现在遇到d2,那么您需要将当前d1-d2 + 1级别上升到当前附加下一个节点之前的分支。

深度d足以识别父级(它必须是深度d-1的最后一个节点),因为在DFS中你不能遇到任何其他父级的深度首先完全探索从前一个分支开始的整个分支。

稍微好一点的证明 - 让v成为深度d列表中的一个节点。它必须下降深度为d-1的某个节点 我们假设列表是

[(v0,d0), ... (v, d), ...]

父级不能位于列表的其余部分,因为这意味着您在其父级之前到达了一个子级 - 在遍历树时不可能。所以父母必须在第一个...部分。我们假设它不是d-1之前的最后v - 深度节点 - 所以让我们说列表是 -

[(v0,d0), ... (vi, d-1), ... (vj, d-1), ...(v, d), ...]

如果vvi的子项,那么当遍历原始树时,您的DFS到达vi,错过了v,然后传递给另一个来自某些分支的分支vi的祖先,在那里找到vj,然后才回到vi并遇到v。这违反了DFS的前提。

答案 2 :(得分:0)

也许我错过了什么,但是:

  • 如果序列中的第一对深度为零,则为预先遍历。
  • 如果序列中的最后一对具有深度零,则它是一个后序遍历。
  • 这是一个有序的遍历。