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. PDF文件添加二维码水印教程

    maven配置iText的jar,主要不是所有私服都有iText的jar,maven仓库没有的,可以去https://mvnrepository.com/artifact/com.itextpdf/i ...

  2. 解决centos ssh连接很慢的问题

    更改配置文件vi /etc/ssh/sshd_config找到UseDNS 将UseDNS前面的#删除,并将YES改为NO,若找不到UseDNS,则手动添加UseDNS,并将其设置成No保存并重启ss ...

  3. hue框架介绍和安装部署

    大家好,我是来自内蒙古的小哥,我现在在北京学习大数据,我想把学到的东西分享给大家,想和大家一起学习 hue框架介绍和安装部署 hue全称:HUE=Hadoop User Experience 他是cl ...

  4. AutoDesk公司搞的fbx模型格式

    FBX® data exchange technology is a 3D asset exchange format that facilitates higher-fidelity data ex ...

  5. eclipse 下载、安装、创建java文件工程、运行---Windows 10

    一.Eclipse Eclipse 是一个开放源代码的.基于Java的可扩展开发平台.就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境.幸运的是,Eclipse 附带了一个标准的插 ...

  6. JAVA 统计字符串中中文,英文,数字,空格,特殊字符的个数

    引言 可以根据各种字符在Unicode字符编码表中的区间来进行判断,如数字为'0'~'9'之间,英文字母为'a'~'z'或'A'~'Z'等,Java判断一个字符串是否有中文是利用Unicode编码来判 ...

  7. SSM整合教程

    接着一直next下去 创建各个目录 pom.xml文件中引入各种包 <?xml version="1.0" encoding="UTF-8"?> & ...

  8. CD 基金会、Jenkins、Jenkins X、Spinnaker 和 Tekton 的常问问题

    转载:https://mp.weixin.qq.com/s/bQLqGrCM9NZYI0Njlu4N-w FAQ 什么是持续交付(CD)? CD是一种软件工程方法,团队在短周期内生成软件,确保软件可以 ...

  9. GAN简介

    GAN Generative Adversarial Networks 生成对抗网络.学习真实世界的真实数据的分布,用于创造以假乱真的数据.比如前段时间很火的应用deep fake.deep nude ...

  10. ASP.NET Core快速入门(第5章:认证与授权)--学习笔记

    课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 任务31:课时介绍 1.Cookie-based认证与授权 2.Cookie- ...