今天是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. PHP get_browser() 函数

    实例 查阅用户的 browscap.ini 文件,并返回用户浏览器的性能: <?phpecho $_SERVER['HTTP_USER_AGENT'];$browser = get_browse ...

  2. PDO::errorCode

    PDO::errorCode — 获取跟数据库句柄上一次操作相关的 SQLSTATE(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0) 说明 语法 mixed PDO: ...

  3. Blob分析之ball_seq.hdev

    * ball_seq.hdev: Inspection of Ball Bonding * 关闭更新dev_update_off ()*图像集合ImageNames := 'die/' + ['die ...

  4. CF804D Expected diameter of a tree 树的直径 根号分治

    LINK:Expected diameter of a tree 1e5 带根号log 竟然能跑过! 容易想到每次连接两个联通快 快速求出直径 其实是 \(max(D1,D2,f_x+f_y+1)\) ...

  5. 学习使用CompletableFuture

    CompletableFuture 一.前言 1.JDK5的异步处理方式 2.JDK8的异步处理方式 二.学习CompletableFuture 1.结果获取方式 2.创建CompletableFut ...

  6. JDK下载和安装教程,超详细

    下载并安装JDK JDK的全称是JavaSE Development Kit,即java开发工具包,是sun公司提供的一套用于开发java应用程序的开发包,它提供了编译.运行java程序所需的各种工具 ...

  7. JS 鼠标放上去滑出内容案例

    .sliderbar { width: 200px; height: 40px; position: relative; margin: 0 auto; } .sliderbar span { dis ...

  8. PHP基础之查找

    前言 之前的文章介绍了PHP的运算符.流程控制.函数.排序等.有兴趣可以去看看. PHP入门之类型与运算符 PHP入门之流程控制 PHP入门之函数 PHP入门之数组 PHP基础之排序 下面简单介绍一下 ...

  9. 2020-05-31:假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?

    福哥答案2020-05-31: 使用keys指令可以扫出指定模式的key列表.对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?这个时候你要回答redis关键的 ...

  10. C#LeetCode刷题之#824-山羊拉丁文​​​​​​​(Goat Latin)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3971 访问. 给定一个由空格分割单词的句子 S.每个单词只包含大 ...