We run a preorder depth first search on the root
of a binary tree.html
At each node in this traversal, we output D
dashes (where D
is the depth of this node), then we output the value of this node. (If the depth of a node is D
, the depth of its immediate child is D+1
. The depth of the root node is 0
.)node
If a node has only one child, that child is guaranteed to be the left child.git
Given the output S
of this traversal, recover the tree and return its root
.github
Example 1:數組
Input: "1-2--3--4-5--6--7" Output: [1,2,5,3,4,6,7]
Example 2:函數
Input: "1-2--3---4-5--6---7" Output: [1,2,5,3,null,6,null,4,null,7]
Example 3:this
Input: "1-401--349---90--88" Output: [1,401,null,349,88,90]
Note:code
1
and 1000
.1
and 10^9
.
這道題讓咱們根據一棵二叉樹的先序遍歷的結果來重建這棵二叉樹,以前有過根據三種遍歷方式-先序,中序,後序中的兩個來重建二叉樹 Construct Binary Tree from Inorder and Postorder Traversal 和 Construct Binary Tree from Preorder and Inorder Traversal,由於一種遍歷方式獲得的結點值數組是沒法惟一的重建出一棵二叉樹的。這裏爲了可以只根據先序遍歷的結果來惟一的重建出二叉樹,提供了每一個結點值的深度,用短槓的個數來表示,根結點的深度爲0,前方沒有短槓,後面的數字前方只有一個短槓的就是根結點的左右子結點,而後緊跟在一個短槓後面的兩個短槓的數字就是根結點左子結點的左子結點,以此類推。並且題目還說了,若某個結點只有一個子結點,那麼必定是左子結點,這就保證了樹結構的惟一性。其實這道題仍是蠻有難度的,輸入只給了一個字符串,咱們不但要把結點值和深度分別提取出來,還要正確的組成樹的結構。因爲先序遍歷的特色,左右子結點的位置可能相隔很遠,就拿例子1來講吧,根結點1的左結點2是緊跟在後面的,可是根結點1的右子結點5卻在很後面,並且有時候也不必定存在右子結點,博主剛開始想的是先查找右子結點的位置,而後調用遞歸,可是發現很差查找,爲啥呢,由於 C++ 中好像沒有 whole match 的查找功能,這裏須要要查找一個槓,且先後位置都不能是槓,後來以爲若樹的深度很大的話,這種處理方式貌似不是很高效。得換一個角度來想問題,咱們在寫非遞歸的先序遍歷的時候,使用了棧來輔助遍歷,這裏一樣也能夠利用棧的後入先出的特色來作。htm
遍歷輸入字符串,先提取短槓的個數,由於除了根結點以外,全部的深度值都是在結點值前面的,全部用一個 for 循環先提取出短槓的個數 level,而後提取結點值,也是用一個 for 循環,由於結點值多是個多位數,有告終點值以後咱們就能夠新建一個結點了。下一步就比較 tricky 了,由於先序遍歷跟 DFS 搜索同樣有一個回到先前位置的過程,好比例子1中,當咱們遍歷到結點5的時候,此時是從葉結點4回到了根結點的右子結點5,如今棧中有4個結點,而當前深度爲1的結點5是要連到根結點的,因此棧中的無關結點就要移除,須要把結點 2,3,4 都移除,就用一個 while 循環,假如棧中元素個數大於當前的深度 level,就移除棧頂元素。那麼此時棧中就只剩根結點了,就能夠鏈接了。此時咱們的鏈接策略是,假如棧頂元素的左子結點爲空,則連在左子結點上,不然連在右子結點上,由於題目中說了,假如只有一個子結點,必定是左子結點。而後再把當前結點壓入棧便可,字符串遍歷結束後,棧中只會留有一個結點(題目中限定了樹不爲空),就是根結點,直接返回便可,參見代碼以下:blog
解法一:
class Solution { public: TreeNode* recoverFromPreorder(string S) { vector<TreeNode*> st; int i = 0, level = 0, val = 0, n = S.size(); while (i < n) { for (level = 0; i < n && S[i] == '-'; ++i) { ++level; } for (val = 0; i < n && S[i] != '-'; ++i) { val = 10 * val + (S[i] - '0'); } TreeNode *node = new TreeNode(val); while (st.size() > level) st.pop_back(); if (!st.empty()) { if (!st.back()->left) st.back()->left = node; else st.back()->right = node; } st.push_back(node); } return st[0]; } };
雖然博主最開始想的遞歸方法不太容易實現,但其實這道題也是能夠用遞歸來作的,這裏咱們須要一個全局變量 cur,表示當前遍歷字符串S的位置,遞歸函數還要傳遞個當前的深度 level。在遞歸函數中,首先仍是要提取短槓的個數,可是這裏有個很 tricky 的地方,咱們在統計短槓個數的時候,不能更新 cur,由於 cur 是個全局變量,當統計出來的短槓個數跟當前的深度不相同,就不能再繼續處理了,若是此時更新了 cur,而沒有正確的復原的話,就會出錯。博主成功入坑,檢查了很久才找出緣由。當短槓個數跟當前深度相同時,咱們繼續提取出結點值,而後新建出結點,對下一層分別調用遞歸函數賦給新建結點的左右子結點,最後返回該新建結點便可,參見代碼以下:
解法二:
class Solution { public: TreeNode* recoverFromPreorder(string S) { int cur = 0; return helper(S, cur, 0); } TreeNode* helper(string& S, int& cur, int level) { int cnt = 0, n = S.size(), val = 0; while (cur + cnt < n && S[cur + cnt] == '-') ++cnt; if (cnt != level) return nullptr; cur += cnt; for (; cur < n && S[cur] != '-'; ++cur) { val = 10 * val + (S[cur] - '0'); } TreeNode *node = new TreeNode(val); node->left = helper(S, cur, level + 1); node->right = helper(S, cur, level + 1); return node; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1028
相似題目:
Construct Binary Tree from Inorder and Postorder Traversal
Construct Binary Tree from Preorder and Inorder Traversal
參考資料:
https://leetcode.com/problems/recover-a-tree-from-preorder-traversal/