题目: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

首先,我们先只看前序遍历

  1. 遇到第一个元素3,那么3肯定是作为根节点
  2. 遇到第二个元素9,那么9可能是左子树也可能是右子树,此时我们结合中序遍历来看,中序遍历的第一个元素是6,那么我们就可以确定9是左子树,因为假如9是右子树,那么中序遍历的第一个元素应该是根节点3,但此时很明显不是3,所以可以确定9是左子树
  3. 再继续往前走,遇到了元素6,同理,6是元素9的左子树,但此时我们发现6与中序遍历第一个元素相等了,这说明左子树已经遍历到了末尾,下一个元素只能是右子树,但究竟是元素6的右子树?还是元素9的右子树?又或者是元素3的右子树?好,我们接着 往下看
  4. 现在是遇到了元素8,我们现在有三种情况
    • 第一种情况:元素8是元素6的右子树,那么此时中序遍历的结果就应该是6、8、9、3...
    • 第二种情况:元素8是元素9的右子树,此时中序遍历的结果应该是6、9、8、3...
    • 第三种情况:元素8是元素3的右子树,此时中序遍历的结果是6、9、3、8...
  5. 我们知道,第二种情况是与我们的中序遍历结果相符合的,所以当我们倒序遍历已经遇到过的元素时,当前遍历的元素8倒序遍历中最后一个相等的元素9的右子树,而符合可以倒序遍历已经遍历过元素的数据结构就是栈,我们可以用栈来存储已经遍历过的元素。
  6. 以此类推,我们可以构造完整棵树
代码
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)的更多相关文章

  1. Java实现 LeetCode 105 从前序与中序遍历序列构造二叉树

    105. 从前序与中序遍历序列构造二叉树 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中 ...

  2. [LeetCode]105. 从前序与中序遍历序列构造二叉树(递归)、108. 将有序数组转换为二叉搜索树(递归、二分)

    题目 05. 从前序与中序遍历序列构造二叉树 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 题解 使用HashMap记录当前子树根节点在中序遍历中的位置,方便每次 ...

  3. 【leetcode 105. 从前序与中序遍历序列构造二叉树】解题报告

    前往 中序,后序遍历构造二叉树, 中序,前序遍历构造二叉树 TreeNode* build(vector<int>& preorder, int l1, int r1, vecto ...

  4. LeetCode 105. 从前序与中序遍历序列构造二叉树(Construct Binary Tree from Preorder and Inorder Traversal)

    题目描述 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9, ...

  5. Leetcode 105. 从前序与中序遍历序列构造二叉树

    题目链接 题目描述 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder ...

  6. leetcode 105从前序与中序遍历序列构造二叉树

    方法一:直接使用复制的数据递归:O(n)时间,O(n)空间,不计算递归栈空间: /** * Definition for a binary tree node. * struct TreeNode { ...

  7. Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序遍历序列构造二叉树

    Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序遍历序列构造二叉树 Leetcode:105. 从前序与中序遍历序列构造二叉树&106. 从中序与后序 ...

  8. LeetCode 中级 - 从前序与中序遍历序列构造二叉树(105)

    一个前序遍历序列和一个中序遍历序列可以确定一颗唯一的二叉树. 根据前序遍历的特点, 知前序序列(PreSequence)的首个元素(PreSequence[0])为二叉树的根(root),  然后在中 ...

  9. 【LeetCode】105#从前序与中序遍历序列构造二叉树

    题目描述 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9 ...

  10. 【2】【leetcode-105,106】 从前序与中序遍历序列构造二叉树,从中序与后序遍历序列构造二叉树

    105. 从前序与中序遍历序列构造二叉树 (没思路,典型记住思路好做) 根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [ ...

随机推荐

  1. 从苏宁电器到卡巴斯基第27篇:难忘的三年硕士时光 V

    一发不可收拾 安全领域的公司都喜欢在看雪或者是吾爱破解这样的网站上发布招聘贴,因为这样的话很容易就能够招到适合的人才,也算是精准营销了.而像我这种想进入安全圈的,也会在这里发布自己的求职简历,以期望能 ...

  2. 【译】N 皇后问题 – 构造法原理与证明 时间复杂度O(1)

    [原] E.J.Hoffman; J.C.Loessi; R.C.Moore The Johns Hopkins University Applied Physics Laboratory *[译]* ...

  3. Maven执行Install命令时跳过测试

    1. 在pom.xml中添加插件 <!-- 跳过单元测试,不然打包的时候会因为加载不了application.yaml报错 --> <plugin> <groupId&g ...

  4. window下批量删除指定后缀文件

    例子: 批量删除当前路径下后缀为 .jpg和 .json del /a /f /s /q "*.jpg" "*.json" *为通配符/a /f 是强制删除所有 ...

  5. ACM、考研、就业,在我心底已经有了明确的答案_人生没有完整的,只有无悔的

    思绪再三,还是决定放弃了ACM,走上考研路(我现在是大二下学期,马上结束).虽然我们ACM的带队老师经常说:"ACM和考研是不冲突的",但是我感觉做ACM和考研的关系不是很紧密,而 ...

  6. (10)MySQL进阶篇SQL优化(InnoDB锁-间隙锁)

    1.概述 当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁:对于键值在条件范围内但并不存在的记录,叫做"间隙(GAP)&quo ...

  7. 使用git rebase去掉无谓的融合

    git pull 預設的行為是將遠端的 repo. 與本地的 repo. 合併,這也是 DVCS 的初衷,將兩個 branch 合併.但是,很多時候會發生以下這種情形: 這是因為,我們團隊的開發模式是 ...

  8. Git 系列教程(12)- 分支的新建与合并

    实际工作场景 可能会遇到的工作流 开发某个网站 为实现某个新的用户需求,创建一个分支 在这个分支上开展新工作 正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补,你将按照如下方式来处理: 切换 ...

  9. 风变编程(Python自学笔记)第12关-我们都是中国人

    1.类的个例叫做实例:类,是对某个群体的统称(类是某个特定的群体),实例是群体中某个具体的个体. 2.Python中的对象等于类和实例的集合. 3. 类的创建:class+类名+冒号,后面语句要缩进. ...

  10. 前端Node的实用方法

    Node 一.什么是Node Node是以基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动.非阻塞式I/O模型(I/O是 input/output的缩写,即输入输出端口,在 ...