我知道有一些方法可以从预订遍历(作为数组)构造树。考虑到有序和预订遍历,更常见的问题是构造它。在这种情况下,虽然顺序遍历是多余的,但它确实使事情变得更容易。任何人都可以告诉我如何进行后期遍历?迭代和递归解决方案都是必需的。
我尝试使用堆栈迭代地执行它,但是根本无法获得正确的逻辑,所以得到了一个可怕的凌乱的树。同样是递归。
答案 0 :(得分:25)
如果你有一个来自BST的后序遍历的数组,你知道根是数组的最后一个元素。根的左子节点占据数组的第一部分,由小于根的条目组成。然后是正确的孩子,由比根大的元素组成。 (两个孩子都可能是空的。)
________________________________
| | |R|
--------------------------------
left child right child root
所以主要问题是要找到左孩子结束和右手开始的点。
这两个孩子也是从他们的后序遍历中获得的,因此以递归方式以相同的方式构建它们。
BST fromPostOrder(value[] nodes) {
// No nodes, no tree
if (nodes == null) return null;
return recursiveFromPostOrder(nodes, 0, nodes.length - 1);
}
// Construct a BST from a segment of the nodes array
// That segment is assumed to be the post-order traversal of some subtree
private BST recursiveFromPostOrder(value[] nodes,
int leftIndex, int rightIndex) {
// Empty segment -> empty tree
if (rightIndex < leftIndex) return null;
// single node -> single element tree
if (rightIndex == leftIndex) return new BST(nodes[leftIndex]);
// It's a post-order traversal, so the root of the tree
// is in the last position
value rootval = nodes[rightIndex];
// Construct the root node, the left and right subtrees are then
// constructed in recursive calls, after finding their extent
BST root = new BST(rootval);
// It's supposed to be the post-order traversal of a BST, so
// * left child comes first
// * all values in the left child are smaller than the root value
// * all values in the right child are larger than the root value
// Hence we find the last index in the range [leftIndex .. rightIndex-1]
// that holds a value smaller than rootval
int leftLast = findLastSmaller(nodes, leftIndex, rightIndex-1, rootval);
// The left child occupies the segment [leftIndex .. leftLast]
// (may be empty) and that segment is the post-order traversal of it
root.left = recursiveFromPostOrder(nodes, leftIndex, leftLast);
// The right child occupies the segment [leftLast+1 .. rightIndex-1]
// (may be empty) and that segment is the post-order traversal of it
root.right = recursiveFromPostOrder(nodes, leftLast + 1, rightIndex-1);
// Both children constructed and linked to the root, done.
return root;
}
// find the last index of a value smaller than cut in a segment of the array
// using binary search
// supposes that the segment contains the concatenation of the post-order
// traversals of the left and right subtrees of a node with value cut,
// in particular, that the first (possibly empty) part of the segment contains
// only values < cut, and the second (possibly empty) part only values > cut
private int findLastSmaller(value[] nodes, int first, int last, value cut) {
// If the segment is empty, or the first value is larger than cut,
// by the assumptions, there is no value smaller than cut in the segment,
// return the position one before the start of the segment
if (last < first || nodes[first] > cut) return first - 1;
int low = first, high = last, mid;
// binary search for the last index of a value < cut
// invariants: nodes[low] < cut
// (since cut is the root value and a BST has no dupes)
// and nodes[high] > cut, or (nodes[high] < cut < nodes[high+1]), or
// nodes[high] < cut and high == last, the latter two cases mean that
// high is the last index in the segment holding a value < cut
while (low < high && nodes[high] > cut) {
// check the middle of the segment
// In the case high == low+1 and nodes[low] < cut < nodes[high]
// we'd make no progress if we chose mid = (low+high)/2, since that
// would then be mid = low, so we round the index up instead of down
mid = low + (high-low+1)/2;
// The choice of mid guarantees low < mid <= high, so whichever
// case applies, we will either set low to a strictly greater index
// or high to a strictly smaller one, hence we won't become stuck.
if (nodes[mid] > cut) {
// The last index of a value < cut is in the first half
// of the range under consideration, so reduce the upper
// limit of that. Since we excluded mid as a possible
// last index, the upper limit becomes mid-1
high = mid-1;
} else {
// nodes[mid] < cut, so the last index with a value < cut is
// in the range [mid .. high]
low = mid;
}
}
// now either low == high or nodes[high] < cut and high is the result
// in either case by the loop invariants
return high;
}
答案 1 :(得分:11)
你真的不需要inorder遍历。只给出了后序遍历,有一种简单的方法来重建树:
这可以通过递归或迭代使用堆栈轻松完成,并且您可以使用两个索引来指示当前子数组的开始和结束,而不是实际拆分数组。
答案 2 :(得分:6)
Postorder遍历如下:
visit left
visit right
print current.
并且像这样:
visit left
print current
visit right
我们举一个例子:
7
/ \
3 10
/ \ / \
2 5 9 12
/
11
依次为:2 3 5 7 9 10 11 12
后序是:2 5 3 9 11 12 10 7
以相反的顺序迭代后序数组并继续将inorder数组拆分为该值的位置。递归地执行此操作,这将是您的树。例如:
current = 7, split inorder at 7: 2 3 5 | 9 10 11 12
看起来很熟悉?左边的子树是左子树,右边的是右子树,就BST结构而言是伪随机顺序。但是,您现在知道您的根是什么。现在对两半做同样的事情。在后序遍历中查找左半部分中元素的第一个匹配项(从末尾开始)。这将是3.分开3:
current = 3, split inorder at 3: 2 | 5 ...
所以你知道你的树到目前为止看起来像这样:
7
/
3
这是基于以下事实:后序遍历中的值将始终出现在其子项出现之后,并且inorder遍历中的值将出现在其子值之间。
答案 3 :(得分:1)
不要循环任何东西。 最后一个元素是你的Root。 然后按顺序排列数组遵循BST的插入规则。
eg:-
given just the postorder -- 2 5 3 9 11 12 10 7
7
\
10
----
7
\
10
\
12
-----
7
\
10
\
12
/
11
-------
7
\
10
/ \
9 12
/
11
--------
7
/ \
3 10
/ \ / \
2 5 9 12
/
11
答案 4 :(得分:0)
没有答案显示工作代码或提供时间复杂度分析,hammar's brilliant answer从来没有。我被挥舞着打扰了,所以让我们开始做些正式的事情。
Hammer在Python中的解决方案:
def from_postorder(nodes: Sequence[int]) -> BinaryTree[int]:
def build_subtree(subtree_nodes: Sequence[int]) -> BinaryTree[int]:
if not subtree_nodes:
return None
n = len(subtree_nodes)
# Locates the insertion point for x to maintain sorted order.
# This is the first element greater than root.
x = bisect.bisect_left(subtree_nodes, subtree_nodes[-1], hi=n - 1)
root = BinaryTree(subtree_nodes[-1])
root.left = build_subtree(subtree_nodes[:x])
# slice returns empty list if end is <= start
root.right = build_subtree(subtree_nodes[x:n - 1])
return root
return build_subtree(nodes)
在每个步骤中,二进制搜索都花费log(n)
时间,并且我们将问题减少一个元素(根)。因此,整体时间复杂度为nlog(n)
。
替代解决方案:
我们创建了两个数据结构,一个是BST的有序遍历,另一个是每个节点到后序遍历序列中其索引的映射。
对于给定范围的形成子树的节点,根是在后遍历中最后出现的根。 为了有效地找到根 ,我们使用之前创建的映射将每个节点映射到其索引,然后找到最大值。
找到根后,我们按有序遍历顺序进行二进制搜索;从给定范围的下边界到根的左侧的元素形成其左子树,从根的右边到范围的右边界的元素形成其右子树。我们在左右子树上递归。
换句话说,我们使用后序遍历序列来找到根,并使用后序遍历序列来找到左和右子树。
时间复杂度:
在每个步骤中,找到根需要O(n)
时间。二进制搜索需要log(n)
时间。我们还将问题分为两个大致相等的子问题(最坏的情况是完整的BST)。因此,T(n) <= 2 . T(n/2) + O(n) + log(n) = T(n/2) + O(n)
使用主定理为我们O(n log(n))
。
def from_postorder_2(nodes: Sequence[int]) -> BinaryTree[int]:
inorder: Sequence[int] = sorted(nodes)
index_map: Mapping[int, int] = dict([(x, i) for i, x in enumerate(nodes)])
# The indices refer to the inorder traversal sequence
def build_subtree(lo: int, hi: int) -> BinaryTree[int]:
if hi <= lo:
return None
elif hi - lo == 1:
return BinaryTree(inorder[lo])
root = max(map(lambda i: index_map[inorder[i]], range(lo, hi)))
root_node = BinaryTree(nodes[root])
x = bisect.bisect_left(inorder, root_node.val, lo, hi)
root_node.left = build_subtree(lo, x)
root_node.right = build_subtree(x + 1, hi)
return root_node
return build_subtree(0, len(nodes))