今天是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 | 基础题,如何不用递归中序遍历二叉树?的更多相关文章

  1. java创建二叉树并实现非递归中序遍历二叉树

    java创建二叉树并递归遍历二叉树前面已有讲解:http://www.cnblogs.com/lixiaolun/p/4658659.html. 在此基础上添加了非递归中序遍历二叉树: 二叉树类的代码 ...

  2. Leetcode 94. Binary Tree Inorder Traversal (中序遍历二叉树)

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

  3. LeetCode OJ:Binary Tree Inorder Traversal(中序遍历二叉树)

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

  4. YTU 2346: 中序遍历二叉树

    原文链接:https://www.dreamwings.cn/ytu2346/2606.html 2346: 中序遍历二叉树 时间限制: 1 Sec  内存限制: 128 MB 提交: 12  解决: ...

  5. Binary Tree Inorder Traversal-非递归实现中序遍历二叉树

    题目描述: 给定一颗二叉树,使用非递归方法实现二叉树的中序遍历 题目来源: http://oj.leetcode.com/problems/binary-tree-inorder-traversal/ ...

  6. Leetcode(105)-从前序与中序遍历序列构造二叉树

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

  7. 094 Binary Tree Inorder Traversal 中序遍历二叉树

    给定一个二叉树,返回其中序遍历.例如:给定二叉树 [1,null,2,3],   1    \     2    /   3返回 [1,3,2].说明: 递归算法很简单,你可以通过迭代算法完成吗?详见 ...

  8. LeetCode:二叉树的非递归中序遍历

    第一次动手写二叉树的,有点小激动,64行的if花了点时间,上传leetcode一次点亮~~~ /* inorder traversal binary tree */ #include <stdi ...

  9. java建立二叉树,递归/非递归先序遍历,递归/非递归中序遍历,层次遍历

    import java.util.LinkedList; import java.util.Scanner; import java.util.Stack; //structure of binary ...

随机推荐

  1. IDEA、maven3.6.3安装、环境配置(windows10)

      maven的安装和配置   1. maven的官方入门指南:http://maven.apache.org/guides/getting-started/index.html 阅读官方文档进行ma ...

  2. MySQL中EXPLAIN命令详细解析

    很多情况下我们需要知道某条SQL语句的性能,都会通过EXPLAIN命令来查看查询优化器是如何执行的. 如何使用 使用EXPLAIN很简单,只需要在执行的SQL前面加上EXPLAIN即可 explain ...

  3. 非常适合小白的 Asyncio 教程

      非常适合小白的 Asyncio 教程 原文:https://segmentfault.com/a/1190000008814676 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束, ...

  4. 使用Flask开发简单接口(2)--POST请求接口

    今天我们继续学习如何使用Flask开发POST接口:用户注册接口和用户登录接口. request接收参数 当我们在页面发出一个POST请求,请求传到服务器时,需要如何拿到当前请求的数据呢?在Flask ...

  5. asp.net 远程模型验证

    有这样一些场景,我们需要模型验证,某些字段不允许重复,但是又不希望在数据访问层增加一堆额外逻辑判断.我们需要数据访问层简洁,这种模型验证在进去Action之前,验证不通过直接告诉前端. 一个特性,继承 ...

  6. Nginx配置SSL证书,提高网络安全性

    首先区别Http与Https HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高 ...

  7. linux常用命令(一)软件操作命令

    软件包管理器:yum 安装软件:yum install xxx 卸载软件:yum remove xxx 搜索软件:yum search xxx 清理缓存:yum clean packages 列出已安 ...

  8. 02-java实现单链表

    02-手撸链表 本篇是恋上数据结构第一季个人总结 借鉴https://juejin.im/post/6844904001478066183#heading-0 本人git https://github ...

  9. Fixing the train-test resolution discrepancy

  10. System.out.println()相关源码

    System.out.println是一个Java语句,一般情况下是将传递的参数,打印到控制台. System:是 java.lang包中的一个final类.根据javadoc,“java.lang. ...