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/