二叉树简介

本篇目录

  • 树的相关概念
  • 树的种类
  • 二叉树的概念和性质
  • 二叉树的广度优先遍历
  • 二叉树的深度优先遍历

  • 树的相关概念

数据结构大致上分为线性结构和非线性结构,线性结构指的是元素之间存在着“一对一”的线性关系的数据结构;非线性结构的逻辑特征是一个结点元素可能对应多个直接前驱和后继结点。树就是一种非线性结构,一般是用来模拟具有树状结构性质的数据集合. 它是由n(n>=1)个有限节点组成一个具有层次关系的集合。

相关概念
子节点:某个节点的后继节点称为它的子节点
父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点

节点的度:一个节点含有的子节点的个数称为该节点的度
叶节点或终端节点:度为零的节点
树的度:一棵树中,最大的节点的度称为树的度

节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推
树的高度或深度:树中节点的最大层次

兄弟节点:具有相同父节点的节点互称为兄弟节点
堂兄弟节点:父节点在同一层的节点互为堂兄弟
节点的祖先:从根到该节点所经分支上的所有节点
子孙:以某节点为根的子树中任一节点都称为该节点的子孙

森林:由m(m>=0)棵互不相交的树的集合称为森林

  • 树的种类

可能你已经注意到了,图片上这颗树的节点被编上了序号。像这样树中任意节点的子节点之间有顺序关系的树称为有序树。树的种类还有很多,不同属性还有交叉性,这里只简单介绍几种常见的形态。

【有序树】:树中任意节点的子节点之间有顺序关系的树。
【二叉树】:每个节点最多含有两个子树的树称为二叉树。
【完全二叉树】:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树。
【满二叉树】:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。也可以这样理解,除叶子结点外的所有结点均有两个子结点。节点数达到最大值,所有叶子结点必须在同一层上。
【平衡二叉树】:当且仅当任何节点的两棵子树的高度差不大于1的二叉树。
【二叉查找树】:又称为二叉排序树或二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
  1) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  2) 若右子树不空,则右子树上所有结点的值均大于或等它的根结点的值;
  3) 左、右子树也分别为二叉排序树;
  4) 没有键值相等的节点。
本文后续的内容,主要围绕二叉树展开。

  • 二叉树的概念和性质

性质1: 在二叉树的第i层上至多有 2^(i-1) 个结点(i>0)
例:第3层最多结点个数 2^(3−1)
性质2: 深度为k的二叉树至多有(2^k) - 1个结点(k>0)
例: 层次2^(3)−1= 7
性质3: 对于任意一棵二叉树,如果其叶结点数为N_0,而度数为2的结点总数为N_2 ,则N_0 = N_2+1
性质4: 最多有n个结点的完全二叉树的深度必为 log2(n+1)
性质5: 对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1 , 其父节点的编号必为i//2(i=1 时为根,除外)
以上的这些性质,在深入学习二叉树时通常都会用到,但是如果没法直接理解也没有关系,我们可以先把树模型用起来,后面再去考虑这些性质。

  • 二叉树的广度优先遍历

广度优先遍历,是指从所在二叉树的根节点出发,首先访问第一层的根节点,然后从左到右访问第二层上的节点,以此类推,自上而下,自左至右逐层访问树的节点的过程。python中可以用链表的形式来模拟二叉树:

点击查看代码
class Node:
    def __init__(self, item):
        self.item = item        # 节点的元素值
        self.lchild = None      # 节点的左子树
        self.rchild = None      # 节点的右子树
class BinaryTree:
    def __init__(self, root = None):  
        self.root = root        # 根节点, 等价于上篇文章链表头结点
在遍历之前,我们先向其中添加一些元素。向二叉树添加元素,我们采用和链表不同的方式。一般来说我们会将新节点添加为某个节点的后继结点,而不去做插入结点。并且,最好是将其从上到下,从左往右的形式添加到最近的空位。这里我们需要用到一个队列来储存结点,以此来依次访问其子节点。代码如下:
点击查看代码
    def add(self, item):
        # 判断根节点是否为空, 如果为空, 当前节点即为根节点
        if self.root == None:
            self.root = Node(item)
            return
        # 定义队列, 把根节点添加到队列中
        queue = []
        queue.append(self.root)
        # 循环判断, 哪个节点的左子树或者右子树为空, 就把新节点添加到哪里.
        while True:
            # 从队列中获取到根节点.
            node = queue.pop(0)
            # 判断当前节点的左子树是否为空.
            if node.lchild == None:
                # 左子树为空, 新节点添加到这里, 程序结束
                node.lchild = Node(item)
                return
            else:
                # 左子树不为空, 就添加到队列中.
                queue.append(node.lchild)
            # 判断当前节点的 右子树是否为空.
            if node.rchild == None:
                # 右子树为空, 新节点添加到这里, 程序结束
                node.rchild = Node(item)
                return
            else:
                # 右子树不为空, 就添加到队列中
                queue.append(node.rchild)

      def breadth_travel(self):
        # 判断如果根结点为空, 直接return
        if self.root == None:
            return
        # 定义队列, 记录节点.
        queue = []
        # 添加根节点到队列中.
        queue.append(self.root)
        # 循环获取, 只要队列有数据(节点), 就循环获取
        while len(queue) > 0:
            # 获取节点信息
            node = queue.pop(0)
            # 打印节点内容.
            print(node.item, end=' ')
            # 判断当前节点是否有左子树, 有就添加到队列
            if node.lchild != None:
                queue.append(node.lchild)
            # 判断当前节点是否有左子树, 有就添加到队列
            if node.rchild != None:
                queue.append(node.rchild)
  • 二叉树的深度优先遍历
    二叉树的深度优先遍历分为三种,分别是前序遍历,中序遍历和后序遍历,指的是在遍历时,是先遍历根节点还是后遍历根节点。无论是先还是后,左节点的遍历优先度都要在右结点之前。例如,采用中序遍历,我们首先去访问根节点的左结点,再访问根节点,再访问右子树。但是如果左结点还有子节点,则还要按照这个顺序先访问左子树的几个结点,再回过头来处理根节点。采用递归的方式可以很方便得模拟这个整个遍历过程,代码如下:
点击查看代码
    def preorder_travel(self, root): 
        # 判断节点是否不为空, 不为空就输出内容.
        if root is not None:
            print(root.item, end=' ')           # 根
            self.preorder_travel(root.lchild)   # 递归获取左子树
            self.preorder_travel(root.rchild)   # 递归获取右子树

    # 遍历二叉树, 深度优先: 中序(左根右)
    def inorder_travel(self, root):
        # 判断节点是否不为空, 不为空就输出内容.
        if root is not None:
            self.inorder_travel(root.lchild) 
            print(root.item, end=' ') 
            self.inorder_travel(root.rchild)  

    # 遍历二叉树, 深度优先: 后序(左右根)
    def postorder_travel(self, root):
        # 判断节点是否不为空, 不为空就输出内容.
        if root is not None:
            self.postorder_travel(root.lchild)  
            self.postorder_travel(root.rchild) 
            print(root.item, end=' ') 

以上,就是我用链表模拟二叉树的过程,谢谢观看~

posted @   小丑与锁鸟  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示