码迷,mamicode.com
首页 > 其他好文 > 详细

Leetcode:Construct Binary Tree from Preorder and Inorder Traversal

时间:2017-11-10 00:37:07      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:实现   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 }
View Code

 

    

 

Leetcode:Construct Binary Tree from Preorder and Inorder Traversal

标签:实现   uil   lis   last   流程   col   下标   iterator   时间复杂度   

原文地址:http://www.cnblogs.com/dalt/p/7811741.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!