Given an integer array with no duplicates. A max tree building on this array is defined as follow:
- The root is the maximum number in the array
- The left subtree and right subtree are the max trees of the subarray divided by the root number.
Construct the max tree by the given array.
Given [2, 5, 6, 0, 3, 1]
, the max tree constructed by this array is:
6
/
5 3
/ /
2 0 1
这是lintcode上的一题,每次先确定根结点最大值,然后根据根结点再分割左右两边,然后继续。按照描述是一个递归过程。但是直接用递归是O(nlogn)的平均复杂度,最坏复杂度为O(n^2),即为有序数组时,但是这题用递归解法在Lintcode上直接爆栈。
所以需要时间和空间复杂度都为O(n)的解法,仔细分析下这题,给出的数组实际是max-tree的一个中序遍历。max-tree有一个特点,每个节点的祖先节点都比其值大.并且一个父亲节点的两个孩子节点在中序遍历中无法紧邻(必须经过父亲节点)所以一个节点左边和右边第一个比它大的都是它的祖先节点.左右比它小的实际是它的孩子子树遍历的结果.
每个结点的父结点都比结点值要大,并且结合中序遍历的性质,如果结点为父结点的左孩子,则在数组中是[结点...父结点]这样的顺序,而如果是右结点,则在数组中是[父结点...结点]这样的顺序。而省略号代表的是结点自己的右子树序列(结点为父结点的左孩子的情况),结点自己的左子树序列(结点为父结点的左孩子的情况),所以...的内容中都是比结点值小的。所以结点的父结点是其左边或者右边第一个比它大的值。在结点是父结点的左右孩子的情况下,如何确定父亲结点是哪个呢。按照推理我们选择其中比较小的那个。因为左右第一个比它大的值都是它的祖先节点.如果我们选择比较大的那个.则另外一个比较小的节点也是这个节点的祖先节点,爷爷节点及以上,所以这个比较小的节点最终成了这个比较大的节点的祖先节点.显然不符合max-tree的特性.
以上得出结论:每个节点的父亲节点,是其左边和右边第一个比它大的数的中的比较小的那个.显然数据结构是使用单调递减栈,时间和空间复杂度都是O(n),代码如下:
""" Definition of TreeNode: class TreeNode: def __init__(self, val): self.val = val self.left, self.right = None, None """ class Solution: # @param A: Given an integer array with no duplicates. # @return: The root of max tree. def maxTree(self, A): # write your code here if not A: return None stack = [] for i in xrange(len(A)+1): right = TreeNode(A[i]) if i < len(A) else TreeNode(sys.maxint) while stack and right.val > stack[-1].val: nowNode = stack.pop() if not stack: right.left = nowNode else: left = stack[-1] if left.val > right.val: right.left = nowNode else: left.right = nowNode stack.append(right) return stack[-1].left
注意最后出栈的值是数组中的最大值,所以为树的根节点,但是最后栈内还有一个值,即sys.maxint生成的节点,该节点的左孩子就是max-tree的根节点.
用了单调栈的性质,但是存的不是数值
步骤:
loop num in A:
1 制作当前current_node = TreeNode(num)
2 loop 如果栈不为空且当前值比peek 大:
那么将peek 置为当前node的左子树
3 如果栈还不为空,那么说明现在peek 比当前值大,就将当前node 置为peek 的右子树
4 把当前node 入栈