二叉树各种遍历方法递归和循环实现(前序、中序、后序、层次)

如果文章可以帮助到您,这是我写文章的荣幸。
这篇文章您可以了解到:

  1. 二叉树的各种遍历方法的简单解释
  2. 遍历方法的递归和循环的python3代码实现

对应的代码位于这里

二叉树的各种遍历方法的简单解释

二叉树顾名思义,最多两个孩子。
一般规定一个二叉树,因为节点间有相互连接的原因,所以只要给定根节点,那么顺着寻找左孩子和右孩子便可以遍历到所有的节点,这就是遍历的直观解释。
而遍历分为深度遍历和广度遍历(具体叫法我没有考证,大家也没有必要太深究一个名字本身,含义更重要是吧 ^.^ ),深度遍历类似于深度搜索的顺序,是深度优先遍历的循序。而广度遍历类似于广度搜索的顺序,是广度优先遍历的循序。我是从这方面区分的,为什么这么分类的原因在之后递归和遍历实现的时候就更能体现这个原因了。
具体看下图:


遍历分类
* 前序遍历:根节点 -> 左子树 -> 右子树
* 中序遍历:左子树 -> 根节点 -> 右子树
* 后序遍历:左子树 -> 右子树 -> 根节点
* 层次遍历:第一层,第二层,...

遍历方法的递归和循环的python代码实现

1.1. 递归方式的前序遍历

使用递归的方式很容易理解:
先排除空节点,然后操作节点,然后左子树同样操作,右子树同样操作
简单解释一下,递归需要有终止条件,也就是空节点的时候。而且对于前序遍历的具体节点的遍历顺序需要明确知道——根节点,根节点的左孩子,然后是左孩子的左孩子,直到没有左孩子,然后是右孩子,然后是右孩子的左孩子...这么描述很难理解,所以之后有例子。
代码实现:

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


def preorderTraversal(root: TreeNode):
    if root is None:
        return
    # do something here, e.g. print itself
    print(root.val)

    preorderTraversal(root.left)
    preorderTraversal(root.right)

很简单,其中的类“TreeNode”的代码之后不再赘述。需要运行一下

if __name__ == '__main__':
    root=TreeNode(1)
    root.left=TreeNode(2)
    root.right=TreeNode(3)
    root.left.left=TreeNode(4)
    root.left.right=TreeNode(5)
    root.right.left=TreeNode(6)
    root.left.left.right=TreeNode(7)
    preorderTraversal(root)

这个就是这样一个二叉树:


二叉树例子

输出是这样的:

1
2
4
7
5
3
6

遍历顺序看图体会一下。

1.2.循环方式的前序遍历

这里插上一句,循环本身和递归是表达的同样一个算法核心,所以呢,不要过于恐惧循环的写法,而且不要太希望递归修改成循环会有很大的速度提升。
我们首先需要处理根节点,然后是根节点的左子树,那么之后处理根节点的右子树的话,我们就需要事先储存根节点,方便之后需要右子树,同理,每一个节点都需要如此操作。而且我们完全处理完一个左子树之后,我们紧接着需要这个左子树的父节点,那么就是最近存储的节点。综上,我们需要的操作是储存节点,而储存的容器是栈,因为后进先出。
为了统一化这种方法(递归转化为循环),规则化一些内容:将现在的节点计算出其他节点称为:扩展^{[1]},对于现在的节点的执行称为:操作,而在栈中的数据是该节点和是否被扩展过。
所以流程就是:

  1. 初始化时将根节点加入栈,标记未扩展
  2. 进行循环,直到栈空
  3. 循环中,取出节点。先判断节点非空,如果标记为未扩展,那么扩展左右孩子加入栈中,先加右孩子,然后是左孩子(因为我们希望的循序是根->左->右,而我们使用的是栈),都标记为未扩展,最后是本节点,标记为扩展;如果标记为扩展,那么进行操作

可能你会学过其他的前序循环的写法,会发现将第三步中这个节点标记是没有意义的,不使用标记一样可以实现(因为这个是一种尾递归的缘由,就是递归部分在操作之后),这没错,但是之后中序后序遍历的时候就要吃苦头了。
按照流程我们编写程序:

def preorderTraversal(root: TreeNode):
    stack=[(root,False)]
    while len(stack)>0:
        tree,extend=stack.pop()
        if tree is None:
            continue
        if not extend:
            stack.append((tree.right,False))
            stack.append((tree.left,False))
            stack.append((tree,True))
        else:
            # do something here, e.g. print itself
            print(tree.val)

当然可以舍弃扩展概念(笔者不推荐):

def preorderTraversal(root: TreeNode):
    stack=[root]
    while len(stack)>0:
        tree=stack.pop()
        if tree is None:
            continue
        stack.append(tree.right)
        stack.append(tree.left)
        # do something here, e.g. print itself
        print(tree.val)

2.1. 递归方式的中序遍历

def inorderTraversal(root: TreeNode):
    if root is None:
        return
    inorderTraversal(root.left)
    # do something here, e.g. print itself
    print(root.val)
    inorderTraversal(root.right)

2.2.循环方式的中序遍历

和之前及其相似,中序后序上的理解和写法的也是很简单的

def inorderTraversal(root: TreeNode):
    stack=[(root,False)]
    while len(stack)>0:
        tree,extend=stack.pop()
        if tree is None:
            continue
        if not extend:
            stack.append((tree.right,False))
            stack.append((tree,True))
            stack.append((tree.left,False))
        else:
            # do something here, e.g. print itself
            print(tree.val)

3.1. 递归方式的后序遍历

def postorderTraversal(root: TreeNode):
    if root is None:
        return
    postorderTraversal(root.left)
    postorderTraversal(root.right)
    # do something here, e.g. print itself
    print(root.val)

3.2.循环方式的后序遍历

def postorderTraversal(root: TreeNode):
    stack=[(root,False)]
    while len(stack)>0:
        tree,extend=stack.pop()
        if tree is None:
            continue
        if not extend:
            stack.append((tree,True))
            stack.append((tree.right,False))
            stack.append((tree.left,False))
        else:
            # do something here, e.g. print itself
            print(tree.val)

3. 层次遍历

这个层次遍历有点特殊,之前的分类中它被分为广度遍历中,因为这个遍历是一层一层的,也就是说,如果使用循环表示的话,它的结构不像栈,而像队列,需要先进先出。
对于递归和循环的转换上,我主张的是递归一定可以转换成循环,而循环不一定能转换成递归,而递归有一种解决到底然后再解决其他问题的特点,也就是深度,需要使用对应循环写法中的栈,这些讨论之后我会出一篇文章仔细讨论,这里提一提,如果认为有兴趣的话,可以关注我一下,这一星期我就会写那一篇。
所以呢,我没有找到层次遍历递归的方法,就算存在这样的方法,也不是很必要,毕竟已经失去递归的简单直观的特点。
所以流程就是:

  1. 初始化时将根节点加入队列
  2. 进行循环,直到队列空
  3. 循环中,取出节点。先判断节点非空,扩展左右孩子加入栈中,先加左孩子,然后是右孩子,并且进行该节点的操作

这个流程相对简单,有点像前序遍历的简单写法

def levelOrderTraversal(root: TreeNode):
    stack=[root]
    while len(stack)>0:
        tree=stack.pop(0)
        if tree is None:
            continue
        stack.append(tree.left)
        stack.append(tree.right)
        # do something here, e.g. print itself
        print(tree.val)

推荐

  1. leetcode中文

  2. leetcode:二叉树的前序遍历

  3. leetcode:二叉树的中序遍历

  4. leetcode:二叉树的层次遍历

5.我的github

5.这篇文章对应的代码

注释

[1]:“现在的节点计算出其他节点称为:扩展[1]”:为什么叫做扩展,是因为节点到其他节点,其他节点的数量可能是多个,也可能是1个,也可能是0个,所以扩展合适一些

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容