algorithm - 反序列化给定格式的树?

标签 algorithm serialization graph tree

我有一串来自二叉树的节点,其序列化格式如下:

# <- the value of a node
(a b c) <- node b has left child a and right child c.

如果一个节点有一个 child ,它总是有两个 child 。所有节点都是独立的,它们的值只是 node.data 的值,因此许多节点可能具有相同的值(但它们仍然是不同的节点)。

例如:

(((1 6 3) 5 (8 1 2)) 10 (1 1 1))

表示树的根节点的值为 10,并且有两个值为 5 和 1 的子节点。值为 5 的子节点有两个子节点,6 和 1。6 有子节点 1 和 3,1 有子节点 8和 2,依此类推。

我试图将其解析为一棵树,但只知道如何通过修剪开始/结束括号然后扫描整个字符串直到 ( 匹配) 的数量。例如:

(((1 6 3) 5 (8 1 2)) 10 (1 1 1)) 

成为

((1 6 3) 5 (8 1 2)) 10 (1 1 1)

所以我扫描,扫描,扫描,并在我阅读 ((1 6 3) 5 (8 1 2)) 后匹配括号计数,这意味着我有左 child ,这意味着下一个角色将是 parent ,之后的一切都将是正确的 child 。递归,递归,等等。除了这种方式,我在每一步都浪费了很多时间重新扫描左边的 child 。

有更好的方法吗?

最佳答案

您没有在这里指定您选择的语言,但这看起来很像 LISP。它有一些很好的方法来处理这些列表,但尽管如此,我还是会尝试给出一个一般性的答案。

首先,这些是递归方法的步骤,在一些类似 Scala 的代码中:

def getLocalRoot(record : String) : Node
{
    val (leftChildrenString, rootPosition) = extractLeft(record)
    val rightChildrenString = extractRight(record, rootPosition)
    val localRootString = extractLocalRoot(record)
    val localRoot = new Node(localRootString) //
    if(leftChildrenString.contains('(')) //a hack, really
       localRoot.left = getLocalRoot(leftChildrenString) //not a leaf
    else
       localRoot.left = new Node(leftChildrenString)  //it is a leaf

    if(rightChildrenString.contains('('))
       localRoot.right=getLocalRoot(rightChildrenString)
    else
       localRoot.right = new Node(rightChildrenString)
    return localRoot
}

def findTreeRoot(serializedTree : String) : Node
{
    return getLocalRoot(serializedTree)
}

((1 5 6) 2 (4 3 0)) 我称粗体部分为“左边的 child ”,右边的部分为“右边的 child ”。

让我们用文字来解释。首先,您需要将字符串拆分为左侧和右侧,即 extractLeftextractRight。我建议您通过从左到右解析字符串并计算括号来执行此操作。一旦计数在右括号后回到 1,下一项就是该子树的根。然后返回左边的部分。您还返回子树根的位置以将其传递给返回右 child 的函数,只是为了加快速度。返回字符串右侧部分的方法实际上应该只返回右侧的所有内容(减去结束 ))。

然后,获取当前本地根,存储它,并在左半部分和右半部分调用相同的方法,但只有如果左半部分或右半部分不是叶子。如果它是叶子,那么您可以使用它来实例化一个新节点,并将其附加到现在找到的父节点。我用了 hack,我只是检查字符串是否包含括号,你可以想出更好的解决方案。

===============替代方法===========

这只需要一次扫描,虽然我不得不用空格填充括号以便我可以更容易地解析它们,但尽管如此,关键是相同的。我基本上使用了一个堆栈。一旦你到达一个封闭的括号,从顶部弹出 3,合并它们,然后将它们推回。

trait Node

case class Leaf(value: String) extends Node

case class ComplexNode(left: Node, value: Leaf, right: Node) extends Node

object Main {

  def main(args: Array[String]) = {
    val stack = new mutable.Stack[Node]
    var input = "(((1 6 3) 5 (8 1 2)) 10 (1 1 1))"
    input = input.replace(")", " ) ").replace("(", " ( ") //just to ease up parsing, it's easier to extract the numbers

    input.split(" ").foreach(word =>
      word match {
        case ")" => {
          stack push collapse(stack)
        }
        case c : String =>  {
          if (c != "(" && c != "") 
             stack.push(Leaf(c))
        }
      }
    )
    println(stack.pop) //you have your structure on the top of the stack
  }

  def collapse(stack: mutable.Stack[Node]): Node = {
    val right = stack.pop
    val parent = stack.pop.asInstanceOf[Leaf]
    val left = stack.pop
    return new ComplexNode(left, parent, right)

  }
}

关于algorithm - 反序列化给定格式的树?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30766343/

相关文章:

graph - Stata:将标签放置在双向图中垂直线的顶部

c++ - 为什么数组被认为比 STL 容器更好?

algorithm - 如何从建筑物中扔出 2 个鸡蛋并用 ~c*sqrt(F) throws 找到 F 层?

algorithm - AStar - 名称解释

c# - 内部类序列化为xml

java - 在哪里放置检查图中两条边是否平行的方法?

java - 使用 floodfill 计算矩阵中的相邻零点

arrays - 如何在剧院中找到二维阵列的最佳座位安排

javascript - 如何将表单数组传递到函数中的 PHP 脚本?

c# - JSON 无法序列化/反序列化数据表列的默认值