标签:实现 uil lis last 流程 col 下标 iterator 时间复杂度
题目大意:分别按先序和中序遍历同一个n结点二叉树,得到两个结点数组P和I。要求利用这些结点数组还原二叉树。
这道题考验对二叉树的理解。先说明一些基础的知识:
先序遍历表示当访问一个结点时,先访问结点值,再访问结点的左孩子,最后访问结点的右孩子。
中序遍历表示当访问一个结点时,先访问结点的左孩子,再访问结点值,最后访问结点的右孩子。(我一开始把先序和中序搞混了)
接下来开始推导一些有用的性质,这里使用p和i分别表示两个函数,对于二叉树结点n,p(n)表示通过先序遍历过程中n的值被访问的次序,i(n)表示通过中续遍历n的值被访问的次序。对于二叉树的根结点root,有p(root)=1。
对于两个结点A和B,假设二者最低的公共祖先结点为C。
性质1:若i(A)<i(B),则必然有以下三个命题之一成立:
A是C的左孩子的后代且B是C的右孩子的后代。(任意一个结点都是自己的后代)
A=C,且B是C的右孩子的后代。
B=C,且A是C的左孩子的后代。
性质2:若p(B)<p(A),则必然有下面三个命题之一成立:
B=C。
B是C的左孩子且A是C的右孩子。
性质3:若A是B的后代,且p(A)=P(B)+1,则必然有命题成立:
A是B的左孩子。
定义1:假设有结点序列F[0],F[1],...,F[k],其中F[0]=B,且F[i]是F[i-1]的父亲,若t是第一个满足条件F[t-1]是F[t]的左孩子的下标,那么称F[t]是B的最近左父。
性质4:若D是B的最近左父,且满足p(B)<p(A),i(B)<i(A)<i(D),那么必然有下面命题成立:
A是B的右孩子的后代。这里稍微说明下:由于i(B)<i(A)<i(D)可以导出A和B是D的左孩子的后代,且A不可能是B的右孩子,而p(B)<p(A)可以导出A不可能是B的父亲。
性质5:若A是B的右孩子的后代,且对于所有满足p(B)<p(X)<p(A)的结点X,都是B的左孩子及其后代,那么A是B的右孩子。
上面所有前提下的推论可以通过枚举讨论得到,这里不细加推导。
现在先说明如何判断一个结点是另外一个结点的左孩子。对于结点A和B,若p(B)+1=p(A)且i(A)<i(B),那么必然有A是B的左孩子。由于性质1和性质2同时被满足,而唯一不矛盾的结论就是B=C且A是B的左孩子的后代,而利用性质3可以得到A是B的左孩子。实际上二者是等价条件。
而对于如何判断一个结点是另外一个结点的右孩子,前面的性质4和性质5已经说的很详细了。
最后说明算法的执行流程。先用一个哈希表实现函数i,之后利用一个可回退的迭代器ITER按顺序遍历数组I。我们建立一个根结点R,假设我们的结果二叉树的根结点是R的左孩子,并认为其i值为I的长度(即先序遍历中被最后访问),这样可以保证每个结点的最近左父的存在。之后利用递归和ITER建立R的左孩子(即结果二叉树)。下面给出建立左右结点的代码:
BuildLeft(ITER, i, father)
if(ITER.hasNext() == false)
return NULL
A = ITER.next()
if(i(A)<i(father) == false)
ITER.previous()
return NULL
A.left = BuildLeft(ITER, i, A)
A.right = BuildRight(ITER, i, A, father)
return A
BuildRight(ITER, i, father, nlAncestor)
if(ITER.hasNext() == false)
return NULL
A = ITER.next()
if(i(father)<i(A) && i(A)<i(nlAncestor) == false)
ITER.previous()
return NULL
A.left = BuildLeft(ITER, i, A)
A.right = BuildRight(ITER, i, A, nlAncestor)
return A
两个函数均通过递归和一些判断对二叉树进行建立。
最后说明一下时间复杂度,对于每一次BuildLeft和BuildRight被调用,都会引起其一次校验不通过或是二叉树中一个结点的建立。校验不通过的情况下,其father必定被加入到了二叉树中,而每一个father最多有两次调用BuildLeft和BuildRight校验不通过的情况,我们将这两次的费用追加在father建立的费用上,由于都是常量级别的费用,因此可以认为每次结点建立都是一个常量时间复杂度的费用。而整个流程最多n个结点被建立加入到二叉树中,故总的时间费用为O(n)。
最后上实际代码,代码的前面两行是由于一开始弄混淆了先序和中序:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 12 13 public TreeNode buildTree(int[] preorder, int[] inorder) { 14 int[] tmp = preorder; 15 preorder = inorder; 16 inorder = tmp; 17 18 Map<Integer, Integer> p = new HashMap(); 19 List<Integer> list = new ArrayList<Integer>(inorder.length); 20 for (int i = 0, bound = preorder.length; i < bound; i++) { 21 Integer v = preorder[i]; 22 p.put(v, i); 23 } 24 25 for(int i = 0, bound = inorder.length; i < bound; i++) 26 { 27 Integer v = inorder[i]; 28 list.add(v); 29 } 30 31 return buildLeft(list.listIterator(), p, preorder.length); 32 } 33 34 public TreeNode buildLeft(ListIterator<Integer> iter, Map<Integer, Integer> p, int parentIndex) { 35 if (!iter.hasNext()) { 36 return null; 37 } 38 39 Integer rootVal = iter.next(); 40 int rootIndex = p.get(rootVal); 41 if (rootIndex > parentIndex) { 42 iter.previous(); 43 return null; 44 } 45 46 TreeNode root = new TreeNode(rootVal); 47 root.left = buildLeft(iter, p, rootIndex); 48 root.right = buildRight(iter, p, rootIndex, parentIndex); 49 50 return root; 51 } 52 53 public TreeNode buildRight(ListIterator<Integer> iter, Map<Integer, Integer> p, int parentIndex, int lastLeftParentIndex) { 54 if (!iter.hasNext()) { 55 return null; 56 } 57 58 Integer rootVal = iter.next(); 59 int rootIndex = p.get(rootVal); 60 if (!(parentIndex < rootIndex && rootIndex < lastLeftParentIndex)) { 61 iter.previous(); 62 return null; 63 } 64 65 TreeNode root = new TreeNode(rootVal); 66 root.left = buildLeft(iter, p, rootIndex); 67 root.right = buildRight(iter, p, rootIndex, lastLeftParentIndex); 68 69 return root; 70 } 71 }
Leetcode:Construct Binary Tree from Preorder and Inorder Traversal
标签:实现 uil lis last 流程 col 下标 iterator 时间复杂度
原文地址:http://www.cnblogs.com/dalt/p/7811741.html