1. 程式人生 > >Leetcode:Construct Binary Tree from Preorder and Inorder Traversal

Leetcode:Construct Binary Tree from Preorder and Inorder Traversal

ide 訪問 false 討論 遍歷 使用 int 叠代 技術

  題目大意:分別按先序和中序遍歷同一個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