如何从已排序的流中最好地构建持久二叉树

Posted

技术标签:

【中文标题】如何从已排序的流中最好地构建持久二叉树【英文标题】:How to best build a persistent binary tree from a sorted stream 【发布时间】:2018-06-19 19:44:13 【问题描述】:

对于一个辅助项目,我想要一种从排序流生成持久二叉搜索树的简单方法。经过一些粗略的搜索后,我只能找到涉及存储排序数组的技术描述,您可以在其中通过索引访问任何元素。我最终写了一些有用的东西,但我认为这是一个很好的领域,一个规范的例子可能记录在某个地方(并且可能有一个名字)。

为了清楚起见,我制作了转换代码。 (也很短)

object TreeFromStream 
  sealed trait ImmutableTree[T] 
    def height: Int
  
  case class ImmutableTreeNode[T](
    value: T,
    left: ImmutableTree[T],
    right: ImmutableTree[T]
  ) extends ImmutableTree[T] 
    lazy val height = left.height + 1
  
  case class NilTree[T]() extends ImmutableTree[T] 
    def height = 0
  

  @tailrec
  def treeFromStream[T](
    stream: Stream[T],
    tree: ImmutableTree[T] = NilTree[T](),
    ancestors: List[ImmutableTreeNode[T]] = Nil
  ): ImmutableTree[T] = 
    (stream, ancestors) match 
      case (Stream.Empty, _) =>
        ancestors.foldLeft(tree)  case(right, root) => root.copy(right=right) 
      case (_, ancestor :: nextAncestors) if ancestor.left.height == tree.height =>
        treeFromStream(stream, ancestor.copy(right=tree), nextAncestors)
      case (next #:: rest, _) => 
        treeFromStream(
          rest, NilTree(),
          ImmutableTreeNode(next, tree, NilTree()) :: ancestors
        )
    
  

【问题讨论】:

en.wikipedia.org/wiki/Self-balancing_binary_search_tree 我不会读或写scala,所以不会评论你的代码。如果您事先知道节点的数量,您可以在 O(n) 时间内在线生成一个complete balanced tree,用于长度为 n 的排序输入。如果事先不知道输入大小,可以生成根的左子树完美,右子树完整的树。如果输入大小是 2^k-1 的形式,那么 while 树将是完美的。该算法将树片段累积在堆栈中,并在新元素到达时将它们缝合在一起。 @Gene:我提前不知道尺寸的情况听起来像是我感兴趣的。你能给我指个引文吗? 你可以看看这篇文章:cglab.ca/~dana/pbst 【参考方案1】:

要创建平衡树,我猜你想这样做,你需要至少访问每个节点一次。首先,将所有节点收集到一个缓冲区中,然后将缓冲区递归转换为树:

  def tfs[T](stream: Stream[T]): ImmutableTree[T] = 
    val ss = scala.collection.mutable.ArrayBuffer.empty[T]
    def treeFromSubsequence(start: Int, end: Int): ImmutableTree[T] =
      if (end == start) NilTree()
      else if (end - start == 1) ImmutableTreeNode(ss(start), NilTree(), NilTree())
      else 
        val mid = (end - start) / 2
        ImmutableTreeNode(ss(mid), treeFromSubsequence(start, mid), treeFromSubsequence(mid + 1, end))
      
    stream.foreach  x => ss += x 
    treeFromSubsequence(0, ss.length)
  

它将准确地访问每个值两次,一次收集它,一次将它放入树的值字段中。

【讨论】:

您不需要预先设置所有节点。最简单的例子是self-balancing binary search tree,其中有很多类型。可能有针对有序插入优化的版本。 您正在对数组中的所有元素进行随机访问。该问题指出,此答案在其他地方找到“经过一些粗略的搜索,我只能找到涉及存储排序数组的技术描述,您可以在其中通过索引访问任何元素。”问题中提供的代码已经可以通过流式传输来完成此操作。 @JimMischel 你能更明确一点吗?哪些实现使用了持久二叉树? @StevenNoble 这些实现都不是持久二叉树,但可以为此目的修改一些。我不是持久数据结构方面的专家,所以我无法提供任何具体细节。我在这里评论的重点是,不需要预先知道节点的数量或节点本身。 如果您不能或不会利用流已排序或节点数已知的知识,那么您将拥有O(N log N) 算法。我提出的算法是O(N)

以上是关于如何从已排序的流中最好地构建持久二叉树的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer题目索引

二叉树的基本概念

数据结构与算法系列研究五——树二叉树三叉树平衡排序二叉树AVL

二叉树二叉树的镜像

树二叉树堆

二叉树二叉查找树