【问题标题】:Finding Time complexity of constructing Binary Search Tree求构建二叉搜索树的时间复杂度
【发布时间】:2015-08-11 16:49:33
【问题描述】:

假设我们有中序遍历顺序和后序遍历。例如: 顺序:30 40 45 50 65 70 80 后订购:30 45 40 65 80 70 50

我知道如何从给定的中序遍历和后序遍历构造二叉搜索树,但我的问题是,如果给出后序遍历,B.S.T 构造的平均和最坏情况时间复杂度是多少?

【问题讨论】:

  • 如果您知道如何实现某些东西,请计算原始操作。如果是递归的,写一个递归。
  • 这取决于您使用的算法。例如,在最坏的情况下,这是在 O (n log n) 中工作的一个:stackoverflow.com/a/13168162/1488799
  • 我们是否可以在 O(n) 中从 Post order traversal 创建 BST?虽然我们需要先排序后序遍历才能知道有序

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


【解决方案1】:

在这两种情况下对于朴素的 BST 构造算法,时间都是 O(n^2),因为:

在有序情况下,算法将添加到右侧

在订单后的情况下,算法将添加到左侧

所以 T(n) = 1 + 2 + 3 + ... n-1 = O(n^2)

UPDATE_3:但在后序情况下,我们可以简单地将每个下一个元素添加为根(前一棵树成为左子),因此复杂度为 O(n)

更新:当然,数字排列的平均时间是 O(n logn),但在这些情况下,这个时间是 O(n^2)(n 是数字的数量)

UPDATE_2:有关更多详细信息,请查看底部的 cmets。

【讨论】:

  • 考虑到这两种遍历,它可能比 O(N^2) 做得更好。请参阅原始帖子的 cmets。
  • 我明白,但我认为他的意思是 BST 收缩算法的基本版本,他正在按顺序插入数字。那么复杂度是 O(n^2)
  • 您答错了问题。您的答案解决了从中序遍历中以天真的方式创建 BST 的时间复杂度。他问的不是这个问题。他询问(虽然不是很好)使用利用后序遍历固有顺序的算法的最佳和最差时间复杂度。
  • 在这种情况下,我们甚至可以记住指向树中最大元素的指针,并在 O(1) 中添加每个元素。当然那么这个BST也会变形。
  • 有 O(n) 个算法可以从两个遍历中构造 BST。 “你回答错了问题”[2]。
【解决方案2】:

您可以使用 O(n) 中的后序遍历来构造树,其原理如下:

  • 最后一个元素总是树根
  • 所有大于根的先前元素都是右子树的一部分。所有较小的元素都是左子树的一部分。
  • 您可以递归地应用上述规则。

按顺序构造更简单。您只需选择中间元素作为根并在两侧递归调用它。

这是一个示例实现(在 Python 中),显示了这两种结构:

from collections import deque

def print_graphviz(tree):
    if not isinstance(tree, tuple):
        return tree
    left = print_graphviz(tree[0])
    right = print_graphviz(tree[2])

    if left is not None: print tree[1], '->', left
    if right is not None: print tree[1], '->', right
    return tree[1]

def visit_post_order(in_queue, limit = None):
    if len(in_queue) == 0 or in_queue[-1] < limit:
        return None
    root = in_queue.pop()
    right = visit_post_order(in_queue, max(root, limit))
    left = visit_post_order(in_queue, limit)

    if left is None and right is None:
        return root
    else:
        return (left, root, right)

def visit_in_order(in_order, begin, end):
    if begin==end: return None
    if begin+1==end: return in_order[begin]

    mid = (begin+end)/2
    root = in_order[mid]
    left = visit_in_order(in_order, begin, mid)
    right = visit_in_order(in_order, mid+1, end)

    if left is None and right is None:
        return root
    else:
        return (left, root, right)

def from_post_order(post_order):
    return visit_post_order(deque(post_order))

def from_in_order(in_order):
    return visit_in_order(in_order, 0, len(in_order))

print 'digraph {'
print print_graphviz(from_post_order([0, 2, 1, 4, 3, 6, 8, 7, 5]))
#print print_graphviz(from_in_order([1, 2, 3, 4, 5, 6, 7, 8]))
print '}'

像这样运行:

python test.py | dot -Tpng | display

你会得到一个漂亮的树输出:

【讨论】:

  • 我是否遗漏了什么,或者如果您只是从最后弹出,为什么您要使用双端队列来处理订单后的情况?如果您不需要双端或从左开始的操作,普通列表会更简单、更快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多