详说二叉树以及广度、深度遍历

树的概念

        树 英语:tree)就是一种非线性结构
        它是用来模拟具有树状结构性质的数据集合. 它是由n(n>=1)个有限节点组成一个具有层次
关系的集合。把它叫做 “树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的.

树的特点

        ①每个节点有零个或多个子节点
        ②没有父节点的节点称为根节点
        ③每一个非根节点有且只有一个父节点
        ④除了根节点外,每个子节点可以分为多个不相交的子树

树的术语

        节点的度:一个节点含有的子节点的个数称为该节点的度
        树的度:一棵树中,最大的节点的度称为树的度
        叶节点或终端节点:度为零的节点
        父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点
        孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点
        兄弟节点:具有相同父节点的节点互称为兄弟节点
        节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推
        树的高度或深度:树中节点的最大层次
        堂兄弟节点:父节点在同一层的节点互为堂兄弟
        节点的祖先:从根到该节点所经分支上的所有节点
        子孙:以某节点为根的子树中任一节点都称为该节点的子孙
        
        森林:由m(m>=0)棵互不相交的树的集合称为森林

树的种类

        无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树
        
        有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树
         二叉树:每个节点最多含有两个子树的树称为二叉树

二叉树的种类

        完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均
已达最大值,且 第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树
        满二叉树的定义是所有叶节点都在最底层的完全二叉树
        平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树
        排序二叉树:
                
                1.若左子树不空,则左子树上所有节点的值均小于它的根节点的值
                2.若右子树不空,则右子树上所有节点的值均大于它的根节点的值
        
                3.左、右子树也分别为二叉排序树

二叉树概念

        二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”

二叉树的性质

        性质1: 在二叉树的第i层上至多有 2i-1 个结点(i>0) eg:第3层最多结点个数 2("#$)
        性质2: 深度为k的二叉树至多有2k - 1个结点(k>0) eg: 层次2(") − 1= 7
        性质3: 任意一棵二叉树,如果其叶结点数为N&,而度数为2的结点总数为N' ,则N& = N'+1
        性质4: 最多有n个结点的完全二叉树的深度必为 log2(n+1)
        性质5: 对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为
2i,其右孩子编号必为2i+1 , 其父节点的编号必为i//2(i=1 时为根,除外)

广度优先遍历

        广度优先可以找到最短路径:
        相当于层次遍历,先把第1层给遍历完,看有没有终点;再把第2层遍历完,看有没有重点。
广度优先----插入节点
        初始操作: 初始化队列、将根节点入队、准备加 入到二叉树的新结点
        重复执行: 获得并弹出队头元素
       
                1、若当前结点的左右子结点不为空,则将其左右子节点入队列
                2、若当前结点的左右子节点为空,则将新结点挂到为空的左子结点、或者右子 节点

深度优先遍历

三种遍历:
        先序遍历:根左右  0 1 3 7 8 4 9 2 5 6
        中序遍历:左根右 7 3 8 1 9 4 0 5 2 6
        后序遍历:左右根 7 8 3 9 4 1 5 6 2 0
遍历结果反推结构
        知道中序遍历和先序遍历或者后序遍历就可以推出二叉树的结构
        思路:
                通过先序遍历可以确定哪个元素是根节点,通过中序遍历可以知道左子树都有那些结
点、右子树都有那些结点。
                1、有了树(先序、中序表示),根据先序确认根节点,中序确定左子树,右子树;有了
左子树和右子树,相当于2颗树
                2、重复步骤1;直到划分完毕

代码实现

# 1.定义Node类,表示节点类
class Node:
    # 初始化节点的信息
    def __init__(self,item):
        self.item = item    # 元素域,数值域:存储区具体数据的
        self.lchild = None    # 链接域,地址域:左子节点
        self.rchild = None    # 链接域,地址域:右子节点

# 2.定义 BinaryTree类,表示:二叉树类
class BinaryTree:
    # 2.1指定二叉树的根节点
    def __init__(self,root=None):
        self.root = root   # 充当根节点

    # 2.2 添加元素
    def add(self,item):
        """
        自定义二叉树添加元素
        :param item: 要添加的数据
        :return:
        """
        # 1.判断根节点是否为空,若为空,直接将当前元素设置为:根节点,程序结束
        if self.root == None:
            self.root = Node(item)
            return
        # 2.走到这里,说明根节点不为空,我们准备列表,用于存储: 二叉树中所有的节点。
        queue = []
        queue.append(self.root)

        # 3. 通过while True, 不断的获取到二叉树中的节点.
        while True:
            cur_node = queue.pop(0)  # 获取节点的.
            # 4. 判断当前节点的 左子树(左子节点)是否为空.
            if cur_node.lchild == None:
                # 4.1 若为空, 则将: 新节点添加为当前节点的 左子树
                cur_node.lchild = Node(item)
                return
            else:
                # 4.2 如果不为空, 则将: 当前节点的左子树加到 队列中.
                queue.append(cur_node.lchild)

            # 5. 判断当前节点的 右子树(右子节点)是否为空.
            if cur_node.rchild == None:
                # 5.1 若为空, 则将: 新节点添加为当前节点的 右子树
                cur_node.rchild = Node(item)
                return
            else:
                # 5.2 如果不为空, 则将: 当前节点的右子树加到 队列中.
                queue.append(cur_node.rchild)

    # 2.3 遍历广度优先
    def breadth_travle(self):
        # 1. 判断根节点是否为空.
        if self.root == None:
            return

        # 2. 创建队列(queue), 用于存储: 二叉树的元素.
        queue = []
        queue.append(self.root)  # 添加根节点到队列中.

        # 3. 循环获取元素(节点), 只要队列不为空(说明还有二叉树节点), 就一直遍历.
        while len(queue) > 0:
            # 3.1 走这里, 说明队列有数据, 我们从(队头)获取元素.
            node = queue.pop(0)
            # 3.2 打印当前节点的内容.
            print(node.item, end=' ')

            # 3.3 判断当前节点是否有左子树, 有就添加到 队列中.
            if node.lchild is not None:
                queue.append(node.lchild)

            # 3.4 判断当前节点是否有右子树, 有就添加到 队列中.
            if node.rchild is not None:
                queue.append(node.rchild)

 # 2.4 遍历, 深度优先 => 前序, 即:  根, 左, 右
    def preorder_travle(self, root):    # root表示节点
        # 1. 判断根节点是否不为空, 不为空, 就按照 根,左,右 顺序逐个获取.
        if root is not None:
            # 2. 根
            print(root.item, end = ' ')
            # 3. 左, 递归获取.
            self.preorder_travle(root.lchild)
            # 4. 右, 递归获取.
            self.preorder_travle(root.rchild)

    # 2.5 遍历, 深度优先 => 中序, 即:  左, 根, 右
    def inorder_travle(self, root):    # root表示节点
        # 1. 判断根节点是否不为空, 不为空, 就按照 左, 根, 右 顺序逐个获取.
        if root is not None:
            # 2. 左, 递归获取.
            self.inorder_travle(root.lchild)
            # 3. 根
            print(root.item, end = ' ')
            # 4. 右, 递归获取.
            self.inorder_travle(root.rchild)

    # 2.6 遍历, 深度优先 => 后序, 即:  左, 右, 根
    def postorder_travle(self, root):    # root表示节点
        # 1. 判断根节点是否不为空, 不为空, 就按照 左, 右, 根 顺序逐个获取.
        if root is not None:
            # 2. 左, 递归获取.
            self.postorder_travle(root.lchild)
            # 3. 右, 递归获取.
            self.postorder_travle(root.rchild)
            # 4. 根
            print(root.item, end = ' ')

# 3.测试代码1,创建节点和二叉树
def test1():
    # 3.1 创建节点
    node = Node('乔峰')
    # 3.2 打印节点
    print(node.item)

    # 3.3 创建二叉树对象
    bt = BinaryTree(node)
    print(bt)   # 二叉树
    print(bt.root)   # 二叉树的根节点
    print(bt.root.item)   # 二叉树的根节点的元素域

# 4 测试代码2 :演示队列,先进先出
def test2():
    # 4.1创建队列.
    queue = []
    # 4.2 往队列中(从队尾)添加元素
    queue.append('A')
    queue.append('B')
    queue.append('C')
    # 4.3 从队列中(从队头)获取元素。
    print(queue.pop(0))
    print(queue.pop(0))
    print(queue.pop(0))

# 5.测试3:测试添加元素到二叉树中, 及广度优先遍历
def test3():
    # 5.1 创建二叉树
    bt = BinaryTree()
    # 5.2 添加元素
    bt.add('A')
    bt.add('B')
    bt.add('C')
    bt.add('D')
    bt.add('E')
    bt.add('F')
    bt.add('G')
    bt.add('H')
    bt.add('I')
    bt.add('J')
    bt.add('K')
    # 5.3 广度优先遍历, 查看结果.
    bt.breadth_travle()

# 6. 测试代码4: 测试添加元素到二叉树中, 及 深度优先遍历.
def test4():
    # 5.1 创建二叉树.
    bt = BinaryTree()
    # 5.2 添加元素.
    bt.add('0')
    bt.add('1')
    bt.add('2')
    bt.add('3')
    bt.add('4')
    bt.add('5')
    bt.add('6')
    bt.add('7')
    bt.add('8')
    bt.add('9')
    # 5.3 广度优先遍历, 查看结果.
    # bt.breadth_travle()     # 广度优先: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    # 5.4 深度优先, 前序
    print('前序: ', end = '')
    bt.preorder_travle(bt.root) # 前序: 0 1 3 7 8 4 9 2 5 6
    print()

    # 5.5 深度优先, 中序
    print('中序: ', end = '')
    bt.inorder_travle(bt.root)  # 中序: 7 3 8 1 9 4 0 5 2 6
    print()

    # 5.6 深度优先, 后序
    print('后序: ', end = '')
    bt.postorder_travle(bt.root) # 后序: 7 8 3 9 4 1 5 6 2 0

# 6.main函数
if __name__ == '__main__':
    #test1()
    #test2()
    #test3()
    test4()

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值