LeetCode 94 | 基础题,如何不用递归中序遍历二叉树?
今天是LeetCode专题第60篇文章,我们一起来看的是LeetCode的94题,二叉树的中序遍历。
这道题的官方难度是Medium,点赞3304,反对只有140,通过率有63.2%,在Medium的题目当中算是很高的了。这题非常基础,可以说是程序员必会的算法题之一。
我们先来看题意。
题意
题意很短, 只有一句话,给定一棵二叉树,返回它中序遍历的结果。
样例
Input: [1,null,2,3]
1
\
2
/
3
Output: [1,3,2]
用递归做这道题非常简单,你能不用递归实现吗?
题解
我们先来介绍一下二叉树的中序遍历,二叉树有三种遍历方式,分别是先序、中序和后序。对于初学者而言,可能会觉得这三种顺序傻傻分不清楚,不知道它们之间有什么分别。其实说白了非常简单,遍历方式其实指的是我们在递归遍历的时候的选择顺序。
假设我们目前递归到的节点是u,它有左右两个孩子。在保证左孩子一定先于右孩子访问的前提下,我们有三种策略。第一种是先把u加入访问序列,之后再分别遍历左右子树,第二种是先递归遍历左子树,然后把u加入访问序列,最后再遍历右子树。第三种策略是先递归遍历左右子树,最后再把u加入访问序列。
这三种策略之间有什么不同呢?其实最大的不同就在于u加入访问序列的顺序不同,如果是先加入u再访问,那么就是先序。如果是先访问了左子树再来加入u,则是中序,最后如果是先递归了左右子树,最后再加入u,则是后序。说白了也就是u先加入就是先序,中间加入就是中序,最后加入就是后序。如果你还觉得有点蒙的话,我们来看下代码就非常清晰了。
# 先序
def dfs(u):
visited.append(u)
dfs(u.left)
dfs(u.right)
# 中序
def dfs(u):
dfs(u.left)
visited.append(u)
dfs(u.right)
# 后序
def dfs(u):
dfs(u.left)
dfs(u.right)
visited.append(u)
但是题目当中要求我们不通过递归来实现,这该怎么办呢?
其实也有办法,我们需要从递归的实现原理入手。我们知道在编译器内部,当我们从一个函数调用另外一个函数的时候,这些函数的信息会被存储在一个栈结构内。栈中间的每一个节点会记录函数的名称以及它目前运行的位置,以及一些中间变量等等。所以当我们递归的时候,其实就是当前的函数不停的入栈的过程。

比如我们dfs函数在第5行代码处递归调用了dfs函数,那么编译器内部的堆栈会记录[(dfs, 5), (dfs, 1)]。如果我们在dfs的第一行又调用了sum函数,那么编译器又会把sum这个函数加入堆栈,变成:[(dfs, 5), (dfs, 1), (sum, 1)]。当函数执行完成之后,会从栈中弹出。
简而言之,递归其实就是利用编译器自行维护的栈结构来简化我们代码和功能的编写。既然这道题当中要求我们不能使用递归,那么我们就只能自己来使用栈来模拟这个过程了。由于我们自己需要维护栈当中的元素,使得整个过程会稍微复杂一些。
在这道题目当中,我们使用栈来记录树上的节点。栈顶的节点即是当前访问的节点。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
ret = []
stack = []
stack.append(root)
while len(stack) > 0:
# 获取栈顶顶点
cur = stack[-1]
if cur is None:
stack.pop()
continue
# 如果左孩子存在,那么优先遍历左孩子
if cur.left is not None:
stack.append(cur.left)
# 把左指针置为空,防止死循环
# 这里也可以用set来维护
cur.left = None
continue
# 左边遍历结束之后加入序列
ret.append(cur.val)
stack.pop()
if cur.right is not None:
stack.append(cur.right)
return ret
总结
如果只是二叉树的遍历,那这个谁都会,但如果不能使用递归,那么就很考验硬实力了。需要我们对递归的底层原理有深入的理解,并且熟悉栈这个数据结构的使用。这段代码的逻辑不难理解,但实现还是挺锻炼人的,推荐大家试试。
今天的文章到这里就结束了,如果喜欢本文的话,请来一波素质三连,给我一点支持吧(关注、转发、点赞)。

- END -
LeetCode 94 | 基础题,如何不用递归中序遍历二叉树?的更多相关文章
- java创建二叉树并实现非递归中序遍历二叉树
java创建二叉树并递归遍历二叉树前面已有讲解:http://www.cnblogs.com/lixiaolun/p/4658659.html. 在此基础上添加了非递归中序遍历二叉树: 二叉树类的代码 ...
- Leetcode 94. Binary Tree Inorder Traversal (中序遍历二叉树)
Given a binary tree, return the inorder traversal of its nodes' values. For example: Given binary tr ...
- LeetCode OJ:Binary Tree Inorder Traversal(中序遍历二叉树)
Given a binary tree, return the inorder traversal of its nodes' values. For example:Given binary tre ...
- YTU 2346: 中序遍历二叉树
原文链接:https://www.dreamwings.cn/ytu2346/2606.html 2346: 中序遍历二叉树 时间限制: 1 Sec 内存限制: 128 MB 提交: 12 解决: ...
- Binary Tree Inorder Traversal-非递归实现中序遍历二叉树
题目描述: 给定一颗二叉树,使用非递归方法实现二叉树的中序遍历 题目来源: http://oj.leetcode.com/problems/binary-tree-inorder-traversal/ ...
- Leetcode(105)-从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树. 注意:你可以假设树中没有重复的元素. 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15, ...
- 094 Binary Tree Inorder Traversal 中序遍历二叉树
给定一个二叉树,返回其中序遍历.例如:给定二叉树 [1,null,2,3], 1 \ 2 / 3返回 [1,3,2].说明: 递归算法很简单,你可以通过迭代算法完成吗?详见 ...
- LeetCode:二叉树的非递归中序遍历
第一次动手写二叉树的,有点小激动,64行的if花了点时间,上传leetcode一次点亮~~~ /* inorder traversal binary tree */ #include <stdi ...
- java建立二叉树,递归/非递归先序遍历,递归/非递归中序遍历,层次遍历
import java.util.LinkedList; import java.util.Scanner; import java.util.Stack; //structure of binary ...
随机推荐
- JavaScript 你真的了解this指向吗
JavaScript 你真的了解this指向吗 前言 终于开始写this指向了,相信这对很多JavaScript的学习者来说是一个非常恐怖的环节,个人认为也算是JavaScript中最难理解的一个知识 ...
- SparkSQL & Spark on Hive & Hive on Spark
刚开始接触Spark被Hive在Spark中的作用搞得云里雾里,这里简要介绍下,备忘. 参考:https://blog.csdn.net/zuochang_liu/article/details/82 ...
- stat 命令家族(1)- 详解 vmstat
性能测试必备的 Linux 命令系列,可以看下面链接的文章哦 vmstat 介绍 Virtual Meomory Statistics,报告虚拟内存统计信息 会统计进程信息.内存.交换区.IO.磁盘. ...
- python5.1文件的读取
fh1=open(r"C:\222.txt","r")#用open函数读取文件,“r”进行转义,fh1文件句柄data=fh1.read()#把读取的句柄赋值给 ...
- jQuery 发布微博案例
.box { width: 500px; height: 500px; border: 1px solid #efefef; margin: 0 auto; text-align: center; } ...
- vue-cookies 使用
import VueCookies from 'vue-cookies' Vue.use(VueCookies) $cookies.config() 设置默认值 $cookies.config(exp ...
- 02【Collection、泛型】
主要内容 Collection集合 迭代器 增强for 泛型 第一章 Collection集合 1.1 集合概述 在前面基础班我们已经学习过并使用过集合ArrayList<E> ,那么集合 ...
- 高级搜索树-伸展树(Splay Tree)
目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...
- C#LeetCode刷题-二分查找
二分查找篇 # 题名 刷题 通过率 难度 4 两个排序数组的中位数 C#LeetCode刷题之#4-两个排序数组的中位数(Median of Two Sorted Arrays)-该题未达最优解 30 ...
- # c++运算符重载之 前置++, 后置++, 负号运算符, 类型转换函数, 以及输入输出运算符
c++运算符重载之 前置++, 后置++, 负号运算符, 类型转换函数, 以及输入输出运算符 标签(空格分隔): c++ 前言 我在c++学习的过程中, 对这几个不太常见的运算符重载不太会写.出现了很 ...