Given a binary tree, return the inorder traversal of its nodes' values.

Example:

Input: [1,null,2,3]
1
\
2
/
3 Output: [1,3,2]

Follow up: Recursive solution is trivial, could you do it iteratively?

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

解法一:

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);
}
};

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

解法二:

// 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 中,因为中序遍历的顺序是左-根-右,参见代码如下:

解法三:

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 {
p = s.top(); s.pop();
res.push_back(p->val);
p = p->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.

就是说线索二叉树实际上是把所有原本为空的右子节点指向了中序遍历顺序之后的那个节点,把所有原本为空的左子节点都指向了中序遍历之前的那个节点,具体例子可以点击这里。那么这道题跟这个线索二叉树又有啥关系呢?由于既不能用递归,又不能用栈,那如何保证访问顺序是中序遍历的左-根-右呢。原来需要构建一个线索二叉树,需要将所有为空的右子节点指向中序遍历的下一个节点,这样中序遍历完左子结点后,就能顺利的回到其根节点继续遍历了。具体算法如下:

1. 初始化指针 cur 指向 root

2. 当 cur 不为空时

  - 如果 cur 没有左子结点

      a) 打印出 cur 的值

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

  - 反之

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

     * 若 pre 不存在右子节点

          a) 将其右子节点指回 cur

        b) cur 指向其左子节点

     * 反之

      a) 将 pre 的右子节点置空

      b) 打印 cur 的值

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

解法四:

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 遍历不仅仅对中序遍历有用,对先序和后序同样有用,具体可参见网友 NOALGO 博客,和 Annie Kim's Blog 的博客。所以对二叉树的三种常见遍历顺序(先序,中序,后序)就有三种解法(递归,非递归,Morris 遍历),总共有九段代码呀,熟练掌握这九种写法才算初步掌握了树的遍历挖~~ 至于二叉树的层序遍历也有递归和非递归解法,至于有没有 Morris 遍历的解法还有待大神们的解答,若真有也请劳烦告知博主一声~~

Github 同步地址:

https://github.com/grandyang/leetcode/issues/94

类似题目:

Validate Binary Search Tree

Binary Tree Preorder Traversal

Binary Tree Postorder Traversal

Binary Search Tree Iterator

Kth Smallest Element in a BST

Closest Binary Search Tree Value II

Inorder Successor in BST

参考资料:

https://leetcode.com/problems/binary-tree-inorder-traversal/

https://leetcode.com/problems/binary-tree-inorder-traversal/discuss/31231/c-ierative-recursive-and-morris-traversal

https://leetcode.com/problems/binary-tree-inorder-traversal/discuss/31213/iterative-solution-in-java-simple-and-readable

https://leetcode.com/problems/binary-tree-postorder-traversal/discuss/45551/preorder-inorder-and-postorder-iteratively-summarization

https://leetcode.com/problems/binary-tree-postorder-traversal/discuss/45621/preorder-inorder-and-postorder-traversal-iterative-java-solution

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Binary Tree Inorder Traversal 二叉树的中序遍历的更多相关文章

  1. [leetcode] 94. Binary Tree Inorder Traversal 二叉树的中序遍历

    题目大意 https://leetcode.com/problems/binary-tree-inorder-traversal/description/ 94. Binary Tree Inorde ...

  2. LeetCode 94. Binary Tree Inorder Traversal 二叉树的中序遍历 C++

    Given a binary tree, return the inorder traversal of its nodes' values. Example: Input: [,,] \ / Out ...

  3. 【LeetCode】Binary Tree Inorder Traversal(二叉树的中序遍历)

    这道题是LeetCode里的第94道题. 题目要求: 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单 ...

  4. Leetcode94. Binary Tree Inorder Traversal二叉树的中序遍历(两种算法)

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

  5. [LeetCode] 94. Binary Tree Inorder Traversal(二叉树的中序遍历) ☆☆☆

    二叉树遍历(前序.中序.后序.层次.深度优先.广度优先遍历) 描述 解析 递归方案 很简单,先左孩子,输出根,再右孩子. 非递归方案 因为访问左孩子后要访问右孩子,所以需要栈这样的数据结构. 1.指针 ...

  6. [LeetCode] Binary Tree Postorder Traversal 二叉树的后序遍历

    Given a binary tree, return the postorder traversal of its nodes' values. For example: Given binary ...

  7. [LeetCode] Binary Tree Preorder Traversal 二叉树的先序遍历

    Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...

  8. [LeetCode] 144. Binary Tree Preorder Traversal 二叉树的先序遍历

    Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...

  9. [Leetcode] Binary tree inorder traversal二叉树中序遍历

    Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...

随机推荐

  1. 基于WebGL 的3D呈现A* Search Algorithm

    http://www.hightopo.com/demo/astar/astar.html 最近搞个游戏遇到最短路径的常规游戏问题,一时起兴基于HT for Web写了个A*算法的WebGL 3D呈现 ...

  2. ASP.NET MVC5中的Model验证

    Model验证是ASP.NET MVC中的重要部分,它主要用于判断输入的数据类型及值是否符合我们设定的规则,这篇文章就介绍下ASP.NET MVC中Model验证的几种方式. 后台验证 DataAnn ...

  3. import matplolib 时出现"This probably means that tk wasn't installed properly."的解决方法

    最近又添了一台新电脑,配置好各个依赖环境后想用matplotlib画个图,结果报出下面的错误 根据报错分析,应该是C:/Python27/tcl/tk8.5/tk.tcl这个文件出问题了,根据图中的信 ...

  4. 【夯实PHP基础】PHP的反射机制

    本文地址 分享提纲: 1. 介绍 2. 具体例子 2.1 创建Persion类 2.2 反射过程 2.3 反射后使用 1. 介绍 -- PHP5添加了一项新的功能:Reflection.这个功能使得p ...

  5. MySQL数据库不能远程访问的解决办法

    MySQL数据库不允许从远程访问怎么办? 下面提供两种方法: 1.改表法 MySQL的帐号不允许从远程登陆,只能在localhost.这个时候只要在localhost的那台电脑,登入mysql后,更改 ...

  6. Maven自定义绑定插件目标:创建项目的源码jar

    <build> <plugins> <!-- 自定义绑定,创建项目的源码jar --> <plugin> <groupId>org.apac ...

  7. 12款简化 Web 开发的 JavaScript 开发框架

    前端框架简化了开发过程中,像 Bootstrap 和 Foundation 就是前端框架的佼佼者.在这篇文章了,我们编制了一组新鲜的,实用的,可以帮助您建立高质量的 Web 应用程序的 JavaScr ...

  8. ES6之解构赋值

    截止到ES6,共有6种声明变量的方法,分别是var .function以及新增的let.const.import和class: 我们通常的赋值方法是: var foo='foo'; function ...

  9. Android中使用ListView实现分页刷新(线程休眠模拟)

    当要显示的数据过多时,为了更好的提升用户感知,在很多APP中都会使用分页刷新显示,比如浏览新闻,向下滑动到当前ListView的最后一条信息(item)时,会提示刷新加载,然后加载更新后的内容.此过程 ...

  10. IOS的七种手势

    今天为大家介绍一下IOS 的七种手势,手势在开发中经常用到,所以就简单 通俗易懂的说下, 话不多说,直接看代码: // 初始化一个UIimageView UIImageView *imageView ...