We run a preorder depth first search on the `root`of a binary tree.

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.)

If a node has only one child, that child is guaranteed to be the left child.

Given the output S of this traversal, recover the tree and return its root.

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:

Input: "1-401--349---90--88"
Output: [1,401,null,349,88,90]

Note:

  • The number of nodes in the original tree is between 1 and 1000.
  • Each node will have a value between 1 and 10^9.

这道题让我们根据一棵二叉树的先序遍历的结果来重建这棵二叉树,之前有过根据三种遍历方式-先序,中序,后序中的两个来重建二叉树 [Construct Binary Tree from Inorder and Postorder Traversal](http://www.cnblogs.com/grandyang/p/4296193.html) 和 [Construct Binary Tree from Preorder and Inorder Traversal](http://www.cnblogs.com/grandyang/p/4296500.html),因为一种遍历方式得到的结点值数组是无法唯一的重建出一棵二叉树的。这里为了能够只根据先序遍历的结果来唯一的重建出二叉树,提供了每个结点值的深度,用短杠的个数来表示,根结点的深度为0,前方没有短杠,后面的数字前方只有一个短杠的就是根结点的左右子结点,然后紧跟在一个短杠后面的两个短杠的数字就是根结点左子结点的左子结点,以此类推。而且题目还说了,若某个结点只有一个子结点,那么一定是左子结点,这就保证了树结构的唯一性。其实这道题还是蛮有难度的,输入只给了一个字符串,我们不但要把结点值和深度分别提取出来,还要正确的组成树的结构。由于先序遍历的特点,左右子结点的位置可能相隔很远,就拿例子1来说吧,根结点1的左结点2是紧跟在后面的,但是根结点1的右子结点5却在很后面,而且有时候也不一定存在右子结点,博主刚开始想的是先查找右子结点的位置,然后调用递归,但是发现不好查找,为啥呢,因为 C++ 中好像没有 whole match 的查找功能,这里需要要查找一个杠,且前后位置都不能是杠,后来觉得若树的深度很大的话,这种处理方式貌似不是很高效。得换一个角度来想问题,我们在写非递归的先序遍历的时候,使用了栈来辅助遍历,这里同样也可以利用栈的后入先出的特点来做。

遍历输入字符串,先提取短杠的个数,因为除了根结点之外,所有的深度值都是在结点值前面的,所有用一个 for 循环先提取出短杠的个数 level,然后提取结点值,也是用一个 for 循环,因为结点值可能是个多位数,有了结点值之后我们就可以新建一个结点了。下一步就比较 tricky 了,因为先序遍历跟 DFS 搜索一样有一个回到先前位置的过程,比如例子1中,当我们遍历到结点5的时候,此时是从叶结点4回到了根结点的右子结点5,现在栈中有4个结点,而当前深度为1的结点5是要连到根结点的,所以栈中的无关结点就要移除,需要把结点 2,3,4 都移除,就用一个 while 循环,假如栈中元素个数大于当前的深度 level,就移除栈顶元素。那么此时栈中就只剩根结点了,就可以连接了。此时我们的连接策略是,假如栈顶元素的左子结点为空,则连在左子结点上,否则连在右子结点上,因为题目中说了,假如只有一个子结点,一定是左子结点。然后再把当前结点压入栈即可,字符串遍历结束后,栈中只会留有一个结点(题目中限定了树不为空),就是根结点,直接返回即可,参见代码如下:

解法一:

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/

https://leetcode.com/problems/recover-a-tree-from-preorder-traversal/discuss/274656/Java-recursive-solution.

https://leetcode.com/problems/recover-a-tree-from-preorder-traversal/discuss/274621/JavaC%2B%2BPython-Iterative-Stack-Solution

[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)

[LeetCode] 1028. Recover a Tree From Preorder Traversal 从先序遍历还原二叉树的更多相关文章

  1. 【leetcode】1028. Recover a Tree From Preorder Traversal

    题目如下: We run a preorder depth first search on the root of a binary tree. At each node in this traver ...

  2. [Swift]LeetCode1028. 从先序遍历还原二叉树 | Recover a Tree From Preorder Traversal

    We run a preorder depth first search on the root of a binary tree. At each node in this traversal, w ...

  3. Binary Tree Inorder/Preorder Traversal 返回中序和前序/遍历二叉树的元素集合

    给定一个二叉树,以集合方式返回其中序/先序方式遍历的所有元素. 有两种方法,一种是经典的中序/先序方式的经典递归方式,另一种可以结合栈来实现非递归 Given a binary tree, retur ...

  4. 【leetcode 968. 1028. 从先序遍历还原二叉树】解题报告[待完善...]

    思路:用一个栈来管理树的层次关系,索引代表节点的深度 方法一: TreeNode* recoverFromPreorder(string S) { /* 由题意知,最上层节点深度为0(数字前面0条横线 ...

  5. [LeetCode] Construct Binary Tree from Preorder and Inorder Traversal 由先序和中序遍历建立二叉树

    Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  6. [LeetCode] 105. Construct Binary Tree from Preorder and Inorder Traversal 由先序和中序遍历建立二叉树

    Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  7. (二叉树 递归) leetcode 889. Construct Binary Tree from Preorder and Postorder Traversal

    Return any binary tree that matches the given preorder and postorder traversals. Values in the trave ...

  8. LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal 由前序和中序遍历建立二叉树 C++

    Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that ...

  9. [LeetCode] 106. Construct Binary Tree from Inorder and Postorder Traversal 由中序和后序遍历建立二叉树

    Given inorder and postorder traversal of a tree, construct the binary tree. Note:You may assume that ...

随机推荐

  1. spring boot 不占用端口方式启动

    随着微服务架构的流行,想要启动一个微服务架构项目就要开启好多端口,有时候一台机器上部署的项目多的时候,端口资源就比较紧张了,其实有的微服务组件仅仅只是提供RPC服务,可以不用占用web启动的端口,此时 ...

  2. 【转】win7旗舰版英文版下载(64位|32位)|Windows7英文版ISO镜像

    Win7旗舰版SP1 64位ISO镜像下载地址:文件名:en_windows_7_enterprise_with_sp1_x64_dvd_u_677651.isoSHA1:A491F985DCCFB5 ...

  3. zabbix 自定义mysql监控

    一.配置zabbix-agent 编辑 /etc/zabbix/zabbix_agentd.conf文件  增加如下两个配置 1.vim /etc/zabbix/zabbix_agentd.conf ...

  4. 使用 jQuery.TypeAhead 让文本框自动完成 (四)(自定义模板)

    项目地址:https://github.com/twitter/typeahead.js 直接贴代码了: @section headSection { <script type="te ...

  5. error: open(".vs/ConsoleApp349/v16/Server/sqlite3/db.lock"): Permission denied error: unable to index file

    第一种1.git add --ignore-errors . 特别注意 git add --ignore-errors . errors后面有一个空格再加一个点' .' 第二种: 1.touch .g ...

  6. HttpHelper之我见

    前几月一直用一个Http的访问类去调用WebApi,说句实话最开始没觉有什么,一是技术老,二是觉得比较简单,但是最近我一直关注云开发和AI这块儿微软技术,看到云平台调用API大多类似,所以回想这个早年 ...

  7. php 除10取整,取十位数前面一个数字,百位前两个数字

    需求:php 除10取整,取十位数前面一个数字,百位前两个数字,并把大于2的加红显示 例:0-9,10-19,20-29,30-39,110-119对应为:0 1 2 3 11 实现主要方法:$num ...

  8. linux的ftp操作

    1.查看是否安装 rpm -qa |grep vsftpd 没有输出,表示没有安装 2.查看服务状态 1).service vsftpd status 输出信息 vsftpd is stopped 表 ...

  9. 常用的linux命令大全

    之前做过两年的运维,用过很多命令,深切体会到某些linux命令熟练掌握后对效率提升有多大.举个简单的例子,在做了研发后经常会有跑一些数据,对于结果数据的处理,我们的产品同学一般都习惯于用excel做统 ...

  10. SMTP 与 IMAP

    (SMTP)邮件传输协议 :电子邮件写好后,由网络传输时的建立在tcp协议基础之上的协议 (IMAP)邮件访问协议:邮件到达目的服务器后,用户从用户的pc ,移动端,等到 接收邮件服务器上去访问自己的 ...