数据结构:树

1.基本概念

1.1 树的定义

树 :是一种非线性结构,是具有树形结构的数据集合;它是由 n 个(n>=0)个节点组成的一个具有层次结构的集合;它具有以下特点:

  • 根节点没有父节点
  • 除根节点以外的节点有且只有一个父节点
  • 每个节点可以有0个或多个子节点,没有子节点的为叶子节点
  • 除根节点以外,每个子节点可以分为多个不相交的子节点

1.2 树的术语

2.书的种类与存储

2.1 树的种类

  • 无序树 :树中任意节点的子节点之间没有顺序关系,这种树称为无序树
  • 有序树 :树中任意节点的子节点之间有顺序关系,这种树称为有序树

有序树又分为:

  • 二叉树:每个节点最多含有两个子树,并且二叉树的子树有左右之分(次序不能颠倒)
  • 哈夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二叉树
  • B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多于两个的子树

2.2 二叉树(重点)

2.2.1 二叉树的概念

二叉树 :每个节点最多含有两个子树,并且二叉树的**子树有左右之分(**次序不能颠倒)

几种特殊的二叉树:

满二叉树 :一棵高度为 h,且有 个结点的二叉树称为满二叉树;即二叉树中的每层都含有最多的结点;满二叉树的叶结点都集中在二叉树的最下一层,并且除叶结点之外的每个结点度数均为 2

可以对满二叉树按层序编号:约定编号从根结点(根结点编号为 1)起,自上而下,自左向右;这样,每个结点对应一个编号,对于编号为 i 的结点,若有双亲,则其双亲为,若有左孩子,则左孩子为 2i;若有右孩子,则右孩子为 2i+1

完全二叉树:高度为h、有n个结点的二叉树,当且仅当其每个结点都与高度为h的满二叉树中编号为1~n 的结点一一对应时,称为完全二叉树,其特点如下:

  • 若,则结点i为分支结点,否则为叶结点
  • 叶结点只可能在层次最大的两层上出现;对于最大层次中的叶结点,都依次排列在该层最左边的位置上
  • 若有度为 1 的结点,则最多只可能有一个,且该结点只有左孩子而无右孩子
  • 按层序编号后,一旦出现某结点(编号为i)为叶结点或只有左孩子,则编号大于 i 的结点均为叶结点
  • 若n为奇数,则每个分支结点都有左孩子和右孩子;若n为偶数,则编号最大的分支结点(编号为 只有左孩子,没有右孩子,其余分支结点左、右孩子都有

二叉排序树:

二叉排序树要么是一个空树,要么是一个具有以下性质的树:

  • 若左子树非空,则左子树上所有节点的值均小于根节点的值
  • 若右子树非空,则右子树上所有节点的值均大于根节点的值
  • 左,右子树也分别是一颗二叉排序树

二叉排序树目的:提高查找,插入和删除关键字的速度,二叉排序树又叫二叉查找树

平衡二叉树:树中任意一个结点的左子树和右子树的高度之差的绝对值不超过 1

注意:平衡二叉树是二叉排序树

为什么需要平衡二叉树:防止在插入数据时树的高度增长过快,降低二叉排序树的性能

正则二叉树:树中每个分支结点都有 2 个孩子,即树中只有度为 0 或 2 的结点。

2.2.1 二叉树的性质

2.2.2 二叉树的存储

2.2.2.1 顺序存储

顺序存储:将二叉树存储在固定的数组中,虽然在遍历速度上有一定优势,但因所占空间较大,是非主流二叉树存储方式

2.2.2.2 链式存储

链式存储 :由于对子节点的个数无法掌握,常见树的存储表示都转换成了二叉树进行处理,子节点个数最多为2

链式存储和顺序存储的优缺点:

链式存储:容易找到子节点

顺序存储:直接存储,没有其他冗余信息,节省空间

2.2.3 二叉树的遍历

2.2.3.1 广度优先遍历

广度优先遍历 = 层序遍历 ,一层一层、从上到下,同层从左往右访问结点,借助队列实现

核心思想:

  1. 先把根结点入队
  2. 循环:队不为空
    1. 队头结点出队,访问该结点
    2. 如果它有左孩子,左孩子入队
    3. 如果它有右孩子,右孩子入队

代码实现:

复制代码
#1.定义二叉树的节点类
class Node(object):
    def __init__(self, item):
        self.left = None
        self.item = item
        self.right = None

    def __str__(self):
        return self.item
#2.定义二叉树类
class BinaryTree(object):
    def __init__(self, root: Node=None):
        self.root = root

    def add(self, item):
        """向二叉树中添加节点,有序添加"""
        node = Node(item)
        if self.root is None:
            self.root = node
        else:
            cur = self.root
            while True:
                if item < cur.item:         # 如果小于当前节点,则往左子树走
                    if cur.left is None:    # 如果当前节点的左子树为空
                        cur.left = node
                        return
                    else:
                        cur = cur.left
                else:                       # 如果大于等于当前节点,则往右子树走
                    if cur.right is None:
                        cur.right = node
                        return
                    else:
                        cur = cur.right

    def breadth_travel(self):
        """广度遍历二叉树"""
        if self.root is None:
                return
        queue = [self.root]
        while queue:
            cur = queue.pop(0)
            print(cur.item)
            if cur.left is not None:
                queue.append(cur.left)
            if cur.right is not None:
                queue.append(cur.right)


def test1_breadth_travel():
    node1 = Node(1)
    tree = BinaryTree(node1)
    tree.add(2)
    tree.add(4)
    tree.add(-1)

    tree.breadth_travel()

#3.测试
if __name__ == '__main__':
    test1_breadth_travel()

注意:抽取方法的快捷键为:Ctrl + Alt + m

2.2.3.2 深度优先遍历

深度优先就是一路向下挖到叶子结点,再回溯,分为 3 种:

  • 先序遍历:根 → 左 → 右
  • 中序遍历:左 → 根 → 右
  • 后序遍历:左 → 右 → 根

代码实现如下:

复制代码
import random

#1.定义二叉树的节点类
class Node(object):
    def __init__(self, item):
        self.left = None
        self.item = item
        self.right = None

    def __str__(self):
        return self.item
#2.定义二叉树类
class BinaryTree(object):
    def __init__(self, root: Node=None):
        self.root = root

    def add(self, item):
        """向二叉树中添加节点,有序添加"""
        node = Node(item)
        if self.root is None:
            self.root = node
        else:
            cur = self.root
            while True:
                if item < cur.item:         # 如果小于当前节点,则往左子树走
                    if cur.left is None:    # 如果当前节点的左子树为空
                        cur.left = node
                        return
                    else:
                        cur = cur.left
                else:                       # 如果大于等于当前节点,则往右子树走
                    if cur.right is None:
                        cur.right = node
                        return
                    else:
                        cur = cur.right

    def breadth_travel(self):
        """广度遍历二叉树"""
        if self.root is None:
                return
        queue = [self.root]                  #根节点入队
        while queue:                         #循环退出条件:根节点为空
            cur = queue.pop(0)               #根节点先出队
            print(cur.item, end=" ")        
            if cur.left is not None:         #左子树不为空
                queue.append(cur.left)
            if cur.right is not None:        #右子树不为空
                queue.append(cur.right)

    # 先序遍历:根 左 右
    def preorder(self, root):
        if root is None:
            return
        print(root.item, end=" ")  # 访问根
        self.preorder(root.left)  # 递归左子树
        self.preorder(root.right)  # 递归右子树

    # 中序遍历:左 根 右
    def inorder(self, root):
        if root is None:
            return
        self.inorder(root.left)  # 递归左子树
        print(root.item, end=" ")  # 访问根
        self.inorder(root.right)  # 递归右子树

    # 后序遍历:左 右 根
    def postorder(self, root):
        if root is None:
            return
        self.postorder(root.left)  # 递归左子树
        self.postorder(root.right)  # 递归右子树
        print(root.item, end=" ")  # 访问根

def tree_generator():
    node1 = Node(random.randint(1, 100))
    tree = BinaryTree(node1)
    for i in range(5):
        tree.add(random.randint(1, 100))
    return tree

def test1_breadth_travel():
    tree = tree_generator()
    tree.breadth_travel()
def test2_preorder():
    tree = tree_generator()
    tree.preorder(tree.root)
def test3_inorder():
    tree = tree_generator()
    tree.inorder(tree.root)
def test4_postorder():
    tree = tree_generator()
    tree.postorder(tree.root)
#3.测试
if __name__ == '__main__':
    test1_breadth_travel()
    print("\n" + "-" * 30 )
    test2_preorder()
    print("\n" + "-" * 30 )
    test3_inorder()
    print("\n" + "-" * 30 )
    test4_postorder()
相关推荐
懂点Tech1 小时前
ctfshow misc28:GIF 高度修改解题与脚本实现
开发语言·windows·python
comedate1 小时前
SMLib 的 Examples 导读:PyAPI / SM_API / SMLib 三组示例怎么读
python·cad·smlib·examples·sm_api
云和数据.ChenGuang2 小时前
Python 重写(Override)深度解析:从 MRO 到 super() 的完整机制
java·开发语言·人工智能·python·langchain·1024程序员节
川石课堂软件测试2 小时前
学习软件测试需掌握的一些知识
数据库·python·sql·功能测试·网络协议·mysql·http
万化冥合3 小时前
PyBox屏幕键盘如何辅助演示键盘操作?
python·计算机外设
车软学堂3 小时前
从测试转开发,到底要补什么
网络·人工智能·python·功能测试·单片机·车载系统·自动化
天空依然蔚蓝3 小时前
分享一个自动检测网站在线状态的python程序
python·运维开发·1024程序员节
路灯上の马陈3 小时前
二叉树的定义与性质
数据结构·二叉树·树结构·1024程序员节
岁月宁静3 小时前
一个 AI 项目从本地到上线,要经过哪些步骤?全流程讲清楚
python·docker·agent
codists4 小时前
《crafting interpreters》阅读笔记:p45-p45
python