基础练习——二叉树及其遍历

1 概念


二叉树是一种树,只不过每个节点最多左右两个子节点(度小于等于2)。

完美二叉树(Perfect Binary Tree)

又叫满二叉树,翻译害人,叶节点都在同一层。

完全二叉树(Complete Binary Tree)

从上到下,从左到右依次填空的二叉树。

完满二叉树(Full Binary Tree)

每个内部节点都有两个子节点,生二胎就完满。

2 构造二叉树


构造方法一

先构造节点,二叉树有个根节点root即可,其它节点直接或间接挂在根上。

class Node():
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

class BinTree():
    def __init__(self, root=None):
        self.root = root

可以先初始化节点,节点里包含着相互关系,选一个作为根节点,一棵树就构造完毕。

构造方法二

构造一种从上往下,从左到右添加节点的方法。树默认有一个-1根节点。

class Node():
    def __init__(self, data=-1, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

class BinTree():
    def __init__(self):
        self.root = Node()
        self.queue = []                 # 用于组织添加节点队列,第1个始终是还没有两个子节点

    def add(self, node):
        if not isinstance(node, Node):
            node = Node(node)

        if self.root.data == -1:
            self.root = node
            self.queue.append(self.root)
        else:
            last_node = self.queue[0]
            if not last_node.left:      # 未初始化,这里需要改进区分
                last_node.left = node
                self.queue.append(last_node.left)
            else:
                last_node.right = node
                self.queue.append(last_node.right)
                self.queue.pop(0)       # 左右子节点都有了,弹出

个人不太喜欢这种方法,节点是否为空、是否初始化容易混淆,可能适合于完全二叉树。

3 遍历


3.1 深度优先

前序遍历

也就是“根-左-右”的顺序,可以使用递归。

    def preorder_trav(self, subtree):
        if subtree is not None:
            print(subtree.data)                 # 根
            self.preorder_trav(subtree.left)    # 左
            self.preorder_trav(subtree.right)   # 右

可以使用堆栈,出栈时存入任务列表。

    def preorder_trav(self, subtree):
        lst = []
        stack = [subtree]
        while stack:
            node = stack.pop()
            lst.append(node)
            if node.right:
                stack.append(node.right)    
            if node.left:
                stack.append(node.left)

        for node in lst:                    # 顺序处理
            print(node.data)

既然是列表,那么出栈时直接处理也一样。这是因为二叉树本身就是从根节点出发的,可以体会下。

    def preorder_trav(self, subtree):
        stack = [subtree]
        while stack:
            node = stack.pop()
            print(node.data)                # 根
            if node.right:
                stack.append(node.right)    # 右
            if node.left:
                stack.append(node.left)     # 左

后序遍历

也就是“左-右-根”的顺序,使用递归。

    def postorder_trav(self, subtree):
        if subtree is not None:
            self.postorder_trav(subtree.left)
            self.postorder_trav(subtree.right)
            print(subtree.data)

使用堆栈的话,因为二叉树的根在上面,所以无法像前序遍历那样,节点刚出栈就处理,而必须开辟一个任务堆栈。

    def postorder_trav(self, subtree):
        lst = []                            # 最终任务堆栈
        stack = [subtree]                   # 展开用的堆栈
        while stack:
            node = stack.pop()
            lst.append(node)                # 根节点压进任务堆栈
            if node.left:
                stack.append(node.left)     # 把左子树先压进展开堆栈,这样在任务堆栈里就是先左节点
            if node.right:
                stack.append(node.right)    # 再压右子树
            
        while lst:
            node = lst.pop()
            print(node.data)

上面的代码,只需要把左右换一下、把任务从lst顺序取出,就变成前序遍历了。

中序遍历

递归的最简单。

    def midorder_trav(self, subtree):
        if subtree is not None:
            self.midorder_trav(subtree.left)
            print(subtree.data)
            self.midorder_trav(subtree.right)

堆栈形式:

    def midorder_trav(self, subtree):
        stack = []
        node = subtree
        while node or stack:
            while node:             # 把左节点都压进栈
                stack.append(node)
                node = node.left
            node = stack.pop()      # 出栈,一个没有左节点的节点
            print(node.data)
            node = node.right       # 如果该节点有右节点,那么刚好是 空 - 中 - 右;
                                    # 否则,该节点自己是左节点,接下来弹出该节点的父节点(中),然后右

3.2 广度优先

层次遍历

可以使用队列,从根节点开始,出队的时候处理节点,同时将其子节点(也就是下一层)逐个进队。这里使用了双端队列deque来实现队列。

from collections import deque
# ...
    def level_trav(self, subtree):
        queue = deque([subtree])
        while queue:
            node = queue.popleft()
            print(node.data)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

也可以直接使用列表list,每处理完一层,同时将下一层的节点存进列表。

    def level_trav(self, subtree):
        lst = [subtree]
        while lst:
            new_lst = []
            for node in lst:
                print(node.data)
                if node.left:
                    new_lst.append(node.left)
                if node.right:
                    new_lst.append(node.right)
            lst = new_lst

4 其它操作


叶子

随便遍历一下即可。

    def leaves(self, subtree):
        leaves = []
        stack = [subtree]
        while stack:
            node = stack.pop()
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)
            if not node.left and not node.right:
                leaves.append(node)
        return leaves

试试递归。

    def leaves(self, subtree):
        if not subtree.left and not subtree.right:
            return [subtree]
        elif subtree.left and not subtree.right:
            return self.leaves(subtree.left)
        elif subtree.right and not subtree.left:
            return self.leaves(subtree.right)
        else:
            return self.leaves(subtree.left) + self.leaves(subtree.right)

高度

递归。

    def height(self, subtree):
        if not subtree:
            return 0
        if not subtree.left and not subtree.right:
            return 1
        elif subtree.left and not subtree.right:
            return self.height(subtree.left) + 1
        elif subtree.right and not subtree.left:
            return self.height(subtree.right) + 1
        else:
            return max(self.height(subtree.left), self.height(subtree.right)) + 1

非递归,遍历时压入深度信息。

    def height(self, subtree):
        height = 1 if subtree else 0
        max_height = height
        stack = [(subtree, height)]
        while stack:
            node, height = stack.pop()
            if node.left:
                stack.append((node.left, height + 1))
            if node.right:
                stack.append((node.right, height + 1))
            if height > max_height:
                max_height = height
        return max_height

左右翻转

递归。

    def reverse(self, subtree):
        if subtree:
            subtree.left, subtree.right = subtree.right, subtree.left
            self.reverse(subtree.left)
            self.reverse(subtree.right)

非递归,类似层序遍历。

    def reverse(self, subtree):
        stack = [subtree]
        while stack:
            node = stack.pop()
            node.left, node.right = node.right, node.left
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)

5 思考


如果二叉树的根在左边,从左子树展开,那当然就可以类似于前序遍历那样了,不过我没见过这样的二叉树结构,估计是这样?可以研究下:

class Node():
    def __init__(self, data, root=None, right=None):
        self.data = data
        self.root = root
        self.right = right

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