【问题标题】:O(n) time non-recursive procedure to traverse a binary tree遍历二叉树的 O(n) 时间非递归过程
【发布时间】:2012-06-14 17:18:11
【问题描述】:

我正在阅读一本名为“算法简介”的书。我想你们很多人都知道。我刚刚碰到一个似乎相当困难的问题:

写一个 O(n) 时间的非递归过程,给定一个 n 节点的二叉树, 打印出每个节点的密钥。在树本身之外使用不超过恒定的额外空间,并且在此过程中不要修改树,即使是临时的。

我看到还有一个类似这样的问题:How to traverse a binary tree in O(n) time without extra memory 但主要区别在于我无法修改树。我正在考虑使用一些访问过的标志,但我还没有提炼出正确的解决方案。这可能是我看不到的显而易见的事情。你将如何设计一种算法来解决这个问题?甚至一些指向答案的指针也将不胜感激。

【问题讨论】:

  • 您可以使用堆栈使用O(logn) 额外内存来完成此操作。没有额外的内存听起来很难。
  • 我不会使用堆栈,因为它不是 O(1) 空间。这也没有意义,因为本书中的前一个练习要求您使用堆栈编写一个非递归遍历过程。
  • 这棵树是搜索树/有序的吗?我们需要一种方法来记住我们已经打印的内容。
  • 正如问题所述,它只是一个n节点二叉树。
  • @ThomasAhle 你假设树是平衡的,否则是O(n) 额外的内存。

标签: algorithm binary-tree


【解决方案1】:

如果树是双向链接的,您可以这样做:

assert root.parent is null

now, old = root, null
while now != null:
    print now.label
    if leaf(now):
        now, old = now.parent, now
    else:
        if old == now.right:
            now, old = now.parent, now
        if old == now.left:
            now, old = now.right, now            
        if old == now.parent:
            now, old = now.left, now

这以根、左、右的顺序打印,但你可以得到任何你喜欢的顺序。

如果树只在一个方向链接,我认为你不能做到这一点。你可以看看Deforestation

【讨论】:

  • 如果我不从根开始,算法必须工作,所以你必须存储入口节点的父节点,如果你到达那个节点,遍历就结束了。
  • 这个算法有缺陷。如果一个节点没有正确的节点,那么 now 将为 null 并且遍历终止。
  • 是的,为了简单起见,它假设总是有两个孩子,但我应该很容易用更多的情况来扩展它,比如“如果只剩下一个孩子 && old == now.left: go up” .
【解决方案2】:

有完整的代码(在 Ruby 中)。

例如,我复制了与“算法简介”一书中相同的“n 节点二叉树”。

class Node
  attr_accessor :key, :parent, :left, :right

  def initialize(key, parent)
    @key = key
    @parent = parent
  end
end

class Tree
  def initialize(root)
    @root = root
  end

  def print_with_constant_space
    current, old = @root, nil
    while current do
      # Going UP
      if old && old.parent == current
        # Go right if exists
        # otherwise continue up
        next_node = (current.right || current.parent)
        current, old = next_node, current

      # Going DOWN
      else
        puts current.key

        # Go left if exists
        # otherwise go right if exists
        # otherwise go up
        next_node = (current.left || current.right || current.parent)
        current, old = next_node, current
      end
    end
  end
end

root         = Node.new(0, nil)
root.left    = (node_1  = Node.new(1, root))
node_1.left  = (node_2  = Node.new(2, node_1))
node_2.right = (node_3  = Node.new(3, node_1))
node_3.left  = (node_4  = Node.new(4, node_3))

node_1.right = (node_5  = Node.new(5, root))
node_5.left  = (node_6  = Node.new(6, node_5))
node_6.right = (node_7  = Node.new(7, node_5))
node_7.right = (node_8  = Node.new(8, node_5))
node_8.left  = (node_9  = Node.new(9, node_8))
node_9.right = (node_10 = Node.new(10, node_8))
node_8.right = (node_11 = Node.new(11, node_5))

node_5.right = (node_12 = Node.new(12, root))
node_12.left = (node_13 = Node.new(13, node_12))

tree = Tree.new(root)
tree.print_with_constant_space

希望对你有帮助……

【讨论】:

    【解决方案3】:

    您必须按顺序遍历树。在同一本书中,在处理二叉搜索树一章的第一组练习中有一个提示。引用:

    有一个使用堆栈作为辅助数据结构的简单解决方案和一个更复杂但优雅的解决方案,它不使用堆栈但假设可以测试两个指针是否相等。

    你可以在这里找到一个实现:http://tech.technoflirt.com/2011/03/04/non-recursive-tree-traversal-in-on-using-constant-space/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-23
      相关资源
      最近更新 更多