LeetCode---105. 从前序与中序遍历序列构造二叉树 (Medium)
题目:105. 从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
第一种解法:递归解
首先
前序遍历: 根 -> 左-> 右
中序遍历:左 -> 根 -> 右
从前序遍历我们可以知道第一个元素3
是根节点,再根据中序遍历我们可以知道从第1
个元素到根节点3
之间的元素是全部都是根节点的左子树,那么我们就可以从中序遍历来得知该根节点左子树元素的个数,假设为x
,从而可以得出根节点左子树元素在前序遍历中的区间是多少,而在知道左子树区间之后,根节点右子树在前序遍历中的区间我们也可以知道,参照下图去理解
比如说下面这棵树:
前序遍历:3 9 6 8 20 15 7
中序遍历:6 9 8 3 15 20 7
3
/ \
9 20
/ \ / \
6 8 15 7
根据代码进一步去理解
代码
class Solution {
// 递归解
int[] preorder;
Map<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
int len = preorder.length;
this.preorder = preorder;
//将中序遍历所有值放在哈希表中,以减少每次在中序遍历中寻找root值下标的时间,空间换时间
for (int i = 0; i < len; i++) {
map.put(inorder[i], i);
}
return buildTree(0, len - 1, 0, len - 1);
}
/**
* @param preLeft : 前序遍历左边界
* @param preRight : 前序遍历右边界
* @param inLeft : 中序遍历左边界
* @param inRight : 中序遍历右边界
* @return
*/
private TreeNode buildTree(int preLeft, int preRight, int inLeft, int inRight) {
// 递归终止条件,如果左边界大于右边界,递归终止,开始向上一层返回结果
if (preLeft > preRight || inLeft > inRight) {
return null;
}
// 获取当前根节点的值
int temp = preorder[preLeft];
// 创建根节点
TreeNode root = new TreeNode(temp);
// 获取根节点在中序遍历中的下标值
int pIndex = map.get(temp);
// 递归获取当前根节点的左子树
// 其中 前序遍历左子树的右边界 = pIndex - 1 - inLeft + preLeft + 1 = pIndex - inLeft + preLeft
root.left = buildTree(preLeft + 1, pIndex - inLeft + preLeft, inLeft, pIndex - 1);
// 递归获取当前根节点的右子树
root.right = buildTree(pIndex - inLeft + preLeft + 1, preRight, pIndex + 1, inRight);
return root;
}
}
第二种解法:迭代
迭代是一种很巧妙地的解法
继续看下面这颗树
前序遍历:3 9 6 8 20 15 7
中序遍历:6 9 8 3 15 20 7
3
/ \
9 20
/ \ / \
6 8 15 7
首先,我们先只看前序遍历
- 遇到第一个元素
3
,那么3
肯定是作为根节点 - 遇到第二个元素
9
,那么9
可能是左子树也可能是右子树,此时我们结合中序遍历来看,中序遍历的第一个元素是6
,那么我们就可以确定9
是左子树,因为假如9
是右子树,那么中序遍历的第一个元素应该是根节点3
,但此时很明显不是3
,所以可以确定9
是左子树 - 再继续往前走,遇到了元素
6
,同理,6
是元素9
的左子树,但此时我们发现6
与中序遍历第一个元素相等了,这说明左子树已经遍历到了末尾,下一个元素只能是右子树,但究竟是元素6
的右子树?还是元素9
的右子树?又或者是元素3
的右子树?好,我们接着 往下看 - 现在是遇到了元素
8
,我们现在有三种情况- 第一种情况:元素
8
是元素6
的右子树,那么此时中序遍历的结果就应该是6、8、9、3...
- 第二种情况:元素
8
是元素9
的右子树,此时中序遍历的结果应该是6、9、8、3...
- 第三种情况:元素
8
是元素3
的右子树,此时中序遍历的结果是6、9、3、8...
- 第一种情况:元素
- 我们知道,第二种情况是与我们的中序遍历结果相符合的,所以当我们倒序遍历已经遇到过的元素时,当前遍历的元素
8
倒序遍历中最后一个相等的元素9
的右子树,而符合可以倒序遍历已经遍历过元素的数据结构就是栈,我们可以用栈来存储已经遍历过的元素。 - 以此类推,我们可以构造完整棵树
代码
class Solution {
//迭代,栈,从后往前遍历解
public TreeNode buildTree2(int[] preorder, int[] inorder) {
Deque<TreeNode> stack = new ArrayDeque<>();
int pre = 0;
int in = 0;
//构造当前正在遍历的节点
TreeNode curRoot = new TreeNode(preorder[pre]);
TreeNode root = curRoot;
stack.push(curRoot);
pre++;
while (pre < preorder.length) {
if (curRoot.val == inorder[in]) {
// 如果当前遍历节点值与中序遍历值相等,不断将栈中元素顶出栈,直到值不相等
while (!stack.isEmpty() && stack.peek().val == inorder[in]) {
curRoot = stack.peek();
stack.pop();
in++;
}
// 当前遍历的节点就是最后一个值相等的节点的右子树
curRoot.right = new TreeNode(preorder[pre]);
curRoot = curRoot.right;
stack.push(curRoot);
pre++;
} else {
//如果值不相等就说明是左子树
curRoot.left = new TreeNode(preorder[pre]);
curRoot = curRoot.left;
stack.push(curRoot);
pre++;
}
}
return root;
}
}
拓展
趁着手热,可以去做一下这道题:106. 从中序与后序遍历序列构造二叉树
LeetCode---105. 从前序与中序遍历序列构造二叉树 (Medium)的更多相关文章
- Java实现 LeetCode 105 从前序与中序遍历序列构造二叉树
105. 从前序与中序遍历序列构造二叉树 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中 ...
- [LeetCode]105. 从前序与中序遍历序列构造二叉树(递归)、108. 将有序数组转换为二叉搜索树(递归、二分)
题目 05. 从前序与中序遍历序列构造二叉树 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 题解 使用HashMap记录当前子树根节点在中序遍历中的位置,方便每次 ...
- 【leetcode 105. 从前序与中序遍历序列构造二叉树】解题报告
前往 中序,后序遍历构造二叉树, 中序,前序遍历构造二叉树 TreeNode* build(vector<int>& preorder, int l1, int r1, vecto ...
- LeetCode 105. 从前序与中序遍历序列构造二叉树(Construct Binary Tree from Preorder and Inorder Traversal)
题目描述 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9, ...
- Leetcode 105. 从前序与中序遍历序列构造二叉树
题目链接 题目描述 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder ...
- leetcode 105从前序与中序遍历序列构造二叉树
方法一:直接使用复制的数据递归:O(n)时间,O(n)空间,不计算递归栈空间: /** * Definition for a binary tree node. * struct TreeNode { ...
- Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序遍历序列构造二叉树
Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序遍历序列构造二叉树 Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序 ...
- LeetCode 中级 - 从前序与中序遍历序列构造二叉树(105)
一个前序遍历序列和一个中序遍历序列可以确定一颗唯一的二叉树. 根据前序遍历的特点, 知前序序列(PreSequence)的首个元素(PreSequence[0])为二叉树的根(root), 然后在中 ...
- 【LeetCode】105#从前序与中序遍历序列构造二叉树
题目描述 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9 ...
- 【2】【leetcode-105,106】 从前序与中序遍历序列构造二叉树,从中序与后序遍历序列构造二叉树
105. 从前序与中序遍历序列构造二叉树 (没思路,典型记住思路好做) 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [ ...
随机推荐
- Portswigger web security academy:Insecure deserialization
Insecure deserialization [toc] Modifying serialized objects 题目描述 此lab使用了 基于序列化的session机制 可以借此进行权限提升 ...
- HR:“最喜欢阿里出来的程序员了,技术又好又耐艹!” 我:???
面试造火箭,进厂拧螺丝?真的是这样吗? 缘起 估计不少同学都是被标题吸引进来的.事先声明,这句话不是我虚构的,而是我实实在在从同事的口中听到的,而且还不止一次. 当时的场景就是很正常的交谈,别人也并没 ...
- Day004 Scanner进阶
Scanner进阶 例1 Scanner scanner = new Scanner(System.in); //从键盘接收数据 int i = 0; float f = 0.0f; System.o ...
- VS Code插件推荐-Settings Sync
Settings Sync功能 将vscode的本地设置.插件保存至远端,方便保存 Usage 插件市场安装Setting Sync之后,⌘+P输入>sync,即可看到相关操作,选中点击之后官方 ...
- Postman报文进行解密之RSA私钥解密
接口返回的数据也是加密的,需要对数据解密才能看到返回的数据是否正确,就需要用RSA解密. 返回数据的解析可以在postman的Tests进行后置处理,获取加密后的返回数据: var data = JS ...
- 如何将代码托管到Github
Github是最火热的源代码管理平台,你可以通过这个平台,将代码托管起来,防止代码丢失,或者将自己的代码展示给世界. 首先要下载Git,请自行在各大应用平台下载. 下载完成后,在你的菜单上下文中就会出 ...
- 发布 .NET 5 带运行时单文件应用时优化文件体积的方法
自 .NET 发布起,.NET Framework 运行环境就是其摆脱不掉的桎梏.后来有了 .NET Core ,微软终于将自带运行时和单文件程序带给了我们.即便如此,大部分情况下开发者仍然不太满意: ...
- Etcd中Raft日志复制的实现
Raft state of log commitIndex : A log entry is committed once the leader that created the entry has ...
- 改善c++程序的150个建议(读后总结)-------12-18
12.优先使用前置操作符 #include <iostream> using namespace std; class A { private: int num; public: A op ...
- InnoDB存储引擎简介
前言: 存储引擎是数据库的核心,对于 MySQL 来说,存储引擎是以插件的形式运行的.虽然 MySQL 支持种类繁多的存储引擎,但最常用的当属 InnoDB 了,本篇文章将主要介绍 InnoDB 存储 ...