Medium!

题目描述:

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
1
\
2
/
3 输出: [1,3,2]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

解题思路:

二叉树的中序遍历顺序为左-根-右,可以有递归和非递归来解,其中非递归解法又分为两种,一种是使用栈来解,另一种不需要使用栈。我们先来看递归方法,十分直接,对左子结点调用递归函数,根节点访问值,右子节点再调用递归函数。

C++解法一:

 // Recursion
class Solution {
public:
vector<int> inorderTraversal(TreeNode *root) {
vector<int> res;
inorder(root, res);
return res;
}
void inorder(TreeNode *root, vector<int> &res) {
if (!root) return;
if (root->left) inorder(root->left, res);
res.push_back(root->val);
if (root->right) inorder(root->right, res);
}
};

下面我们再来看非递归使用栈的解法,也是符合本题要求使用的解法之一,需要用栈来做,思路是从根节点开始,先将根节点压入栈,然后再将其所有左子结点压入栈,然后取出栈顶节点,保存节点值,再将当前指针移到其右子节点上,若存在右子节点,则在下次循环时又可将其所有左子结点压入栈中。这样就保证了访问顺序为左-根-右。

C++解法二:

 // Non-recursion
class Solution {
public:
vector<int> inorderTraversal(TreeNode *root) {
vector<int> res;
stack<TreeNode*> s;
TreeNode *p = root;
while (p || !s.empty()) {
while (p) {
s.push(p);
p = p->left;
}
p = s.top();
s.pop();
res.push_back(p->val);
p = p->right;
}
return res;
}
};

下面这种解法跟Binary Tree Preorder Traversal中的解法二几乎一样,就是把结点值加入结果res的步骤从if中移动到了else中,因为中序遍历的顺序是左-根-右。

C++解法三:

 class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> s;
TreeNode *p = root;
while (!s.empty() || p) {
if (p) {
s.push(p);
p = p->left;
} else {
TreeNode *t = s.top(); s.pop();
res.push_back(t->val);
p = t->right;
}
}
return res;
}
};

下面我们来看另一种很巧妙的解法,这种方法不需要使用栈,所以空间复杂度为常量,这种非递归不用栈的遍历方法有个专门的名字,叫Morris Traversal,在介绍这种方法之前,我们先来引入一种新型树,叫 Threaded binary tree,这个还不太好翻译,我第一眼看上去以为是叫线程二叉树,但是感觉好像又跟线程没啥关系,后来看到网上有人翻译为螺纹二叉树,就暂且叫螺纹二叉树吧。我们先来看看维基百科上关于它的英文定义:

A binary tree is threaded by making all right child pointers that would normally be null point to the inorder successor of the node (if it exists), and all left child pointers that would normally be null point to the inorder predecessor of the node.

就是说螺纹二叉树实际上是把所有原本为空的右子节点指向了中序遍历顺序之后的那个节点,把所有原本为空的左子节点都指向了中序遍历之前的那个节点,具体例子可参见https://en.wikipedia.org/wiki/Threaded_binary_tree。

那么这道题跟这个螺纹二叉树又有啥关系呢?由于我们既不能用递归,又不能用栈,那我们如何保证访问顺序是中序遍历的左-根-右呢。原来我们需要构建一个螺纹二叉树,我们需要将所有为空的右子节点指向中序遍历的下一个节点,这样我们中序遍历完左子结点后,就能顺利的回到其根节点继续遍历了。具体算法如下:

1. 初始化指针cur指向root

2. 当cur不为空时

  - 如果cur没有左子结点

      a) 打印出cur的值

    b) 将cur指针指向其右子节点

  - 反之

     将pre指针指向cur的左子树中的最右子节点 

     * 若pre不存在右子节点

          a) 将其右子节点指回cur

        b) cur指向其左子节点

     * 反之

      a) 将pre的右子节点置空

      b) 打印cur的值

      c) 将cur指针指向其右子节点

C++解法四:

 // Non-recursion and no stack
class Solution {
public:
vector<int> inorderTraversal(TreeNode *root) {
vector<int> res;
if (!root) return res;
TreeNode *cur, *pre;
cur = root;
while (cur) {
if (!cur->left) {
res.push_back(cur->val);
cur = cur->right;
} else {
pre = cur->left;
while (pre->right && pre->right != cur) pre = pre->right;
if (!pre->right) {
pre->right = cur;
cur = cur->left;
} else {
pre->right = NULL;
res.push_back(cur->val);
cur = cur->right;
}
}
}
return res;
}
};

其实Morris遍历不仅仅对中序遍历有用,对先序和后序同样有用,具体可参见http://noalgo.info/832.html和http://www.cnblogs.com/AnnieKim/archive/2013/06/15/morristraversal.html,所以对二叉树的三种常见遍历顺序(先序,中序,后序)就有三种解法(递归,非递归,Morris遍历),总共有九段代码呀,熟练掌握这九种写法才算初步掌握了树的遍历挖~~ 至于二叉树的层序遍历也有递归和非递归解法,至于有没有Morris遍历的解法还有待大神们的解答。

LeetCode(94):二叉树的中序遍历的更多相关文章

  1. LeetCode 94. 二叉树的中序遍历(Binary Tree Inorder Traversal)

    94. 二叉树的中序遍历 94. Binary Tree Inorder Traversal 题目描述 给定一个二叉树,返回它的 中序 遍历. LeetCode94. Binary Tree Inor ...

  2. Java实现 LeetCode 94 二叉树的中序遍历

    94. 二叉树的中序遍历 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? / ...

  3. Leetcode 94. 二叉树的中序遍历

    1.问题描述 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 2.解法一 ...

  4. LeetCode 94. 二叉树的中序遍历(Binary Tree Inorder Traversal)

    题目描述 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 解题思路 由于 ...

  5. leetcode 94二叉树的中序遍历

    递归算法C++代码: /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; ...

  6. 【leetcode 94. 二叉树的中序遍历】解题报告

    前往二叉树的:前序,中序,后序 遍历算法 方法一:递归 vector<int> res; vector<int> inorderTraversal(TreeNode* root ...

  7. LeetCode 94 ——二叉树的中序遍历

    1. 题目 2. 解答 2.1. 递归法 定义一个存放树中数据的向量 data,从根节点开始,如果节点不为空,那么 递归得到其左子树的数据向量 temp,将 temp 合并到 data 中去 将当前节 ...

  8. 【LeetCode】94. 二叉树的中序遍历

    94. 二叉树的中序遍历 知识点:二叉树:递归:Morris遍历 题目描述 给定一个二叉树的根节点 root ,返回它的 中序 遍历. 示例 输入:root = [1,null,2,3] 输出:[1, ...

  9. Leetcode题目94.二叉树的中序遍历(中等)

    题目描述: 给定一个二叉树,返回它的中序遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 思路解析: 1 ...

  10. leetcode刷题-94二叉树的中序遍历

    题目 给定一个二叉树,返回它的中序 遍历. 实现 # def __init__(self, x): # self.val = x # self.left = None # self.right = N ...

随机推荐

  1. LOJ #2183「SDOI2015」序列统计

    有好多好玩的知识点 LOJ 题意:在集合中选$ n$个元素(可重复选)使得乘积模$ m$为$ x$,求方案数对$ 1004535809$取模 $ n<=10^9,m<=8000且是质数,集 ...

  2. H - Rescue the Princess ZOJ - 4097 (tarjan缩点+倍增lca)

    题目链接: H - Rescue the Princess  ZOJ - 4097 学习链接: zoj4097 Rescue the Princess无向图缩点有重边+lca - lhc..._博客园 ...

  3. js中创建数组,并往数组里添加元素

    数组的创建 var arrayObj = new Array(); //创建一个数组 var arrayObj = new Array([size]); //创建一个数组并指定长度,注意不是上限,是长 ...

  4. 解决Ubuntu 18.04中文输入法的问题,安装搜狗拼音

    首先安装fcitx一.检测是否安装fcitx首先检测是否有fcitx,因为搜狗拼音依赖fcitx> fcitx提示:程序“fcitx”尚未安装. 您可以使用以下命令安装:> sudo ap ...

  5. JAVA并行异步编程,线程池+FutureTask

    java 在JDK1.5中引入一个新的并发包java.util.concurrent 该包专门为java处理并发而书写. 在java中熟悉的使用多线程的方式为两种?继续Thread类,实现Runnal ...

  6. SpringSecurity实现图形验证码功能

    ⒈封装验证码类 package cn.coreqi.security.validate; import java.awt.image.BufferedImage; import java.time.L ...

  7. for..of和for..in和map、filter等循环区别

    1.for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值. for in遍历比较适合遍历对象,不太适合数组,有可能遍历出来的不按照顺序 遍历数组 ,,,,,] for (var ...

  8. 题解-SDOI2015 约数个数和

    Problem bzoj3994 洛谷3327 题意:设 \(d(x)\) 为 \(x\) 的约数个数,给定 \(N,M\),求\(\sum_{i=1}^N\sum_{j=1}^Md(ij)\) \( ...

  9. Windows下文件夹扩展名

    回收站.{645ff040-5081-101b-9f08-00aa002f954e} 拨号网络.{992CFFA0-F557-101A-88EC-00DD010CCC48} 打印机.{2227a280 ...

  10. eclipse 迁移项目 乱码

    eclipse 迁移项目总是乱码问题,网上解决都无非是把workspace.项目.文件等改成utf-8,但总是不好使,因为原来有的文件类型还是要改成原来的编码格式,可以使用文本工具如notepad打开 ...