二叉树详细介绍与代码生成遍历

news2024/9/19 10:55:46

目录

  • 树的概念及其结构
    • 树的构造——代码表示
  • 二叉树概念及介绍
  • 二叉树的存储结构
    • 二叉树的顺序结构
    • 二叉树的链式结构
    • 链表的代码展示
    • 堆的基本概念和结构
    • 堆的代码体现
    • 二叉树生成
    • 二叉树遍历
  • 四种不同遍历方式——代码展示

树的概念及其结构

要了解二叉树,那么首要的就是要知道树的概念。
在这里插入图片描述

在计算机科学中,树是一种广泛使用的抽象数据类型,用于模拟层次结构,树由节点构成。在这种层级系统中,存在一个最顶层的节点,称为根节点,它没有父节点。树中的其他节点可以有一个(在非二叉树中)或多个子节点,但只有一个父节点,除了根节点外。

树结构的基本术语:

  • 节点(Node): 树的基本部分,可能包含一个值或数据,并可能有子节点。
  • 根节点(Root): 树的顶端节点,没有父节点。
  • 叶子节点(Leaf nodes): 没有子节点的节点。
  • 子节点(Child nodes): 某个节点的直接后继节点。
  • 父节点(Parent node): 某个节点的直接前驱节点。
  • 兄弟节点(Sibling nodes): 具有相同父节点的节点。
  • 边(Edge): 连接节点的线,它定义了节点之间的父子关系。
  • 路径(Path): 从一个节点到另一个节点的边的序列。
  • 深度(Depth): 从根节点到某个节点的边的数目。
  • 高度(Height): 一个节点到最远叶子节点的最长路径的边的数目。树的高度是其所有节点高度的最大值。

下图是树的结构示意图
在这里插入图片描述
树结构没有循环或闭环,这是它和图结构的主要区别,如下就不能称作是树结构,因为子树之间有相交。
在这里插入图片描述

树的构造——代码表示

class TreeNode:
    def __init__(self, value):
        self.value = value  # 当前节点的值
        self.children = []  # 子节点的列表

    def add_child(self, child_node):
        """添加子节点"""
        self.children.append(child_node)

    def remove_child(self, child_node):
        """移除子节点"""
        self.children = [child for child in self.children if child is not child_node]

    def traverse(self):
        """遍历树节点"""
        nodes_to_visit = [self]
        while len(nodes_to_visit) > 0:
            current_node = nodes_to_visit.pop()
            print(current_node.value)
            nodes_to_visit.extend(current_node.children)

# 示例构造树
root = TreeNode('Root')
child1 = TreeNode('Child1')
child2 = TreeNode('Child2')
child3 = TreeNode('Child3')

root.add_child(child1)
root.add_child(child2)
child1.add_child(child3)

# 遍历和打印树
root.traverse()

上述代码定义了一个树节点,它包含一个值和一个子节点列表。并且可以添加或移除子节点,并有一个用于遍历树的方法,它采用深度优先遍历(Depth-First Search, DFS)的方式来访问每个节点。

这个例子中构建的树结构如下所示:
在这里插入图片描述
当运行 root.traverse() 方法时,它会打印出:

Root
Child1
Child3
Child2

这不是二叉树,因为每个节点可以有任意数量的子节点。 若要创建一个二叉树的结构,你需要为每个节点定义两个指定的子节点属性,通常是 left 和 right。

二叉树概念及介绍

二叉树是一种重要的数据结构,在计算机科学中广泛应用于各类算法中。二叉树是由节点组成的,每个节点最多有两个子节点,分别称为左子节点和右子节点。

二叉树的类别:

1、完全二叉树(Complete Binary Tree): 除了最后一层外,每一层都被完全填满,最后一层的所有节点都尽可能地靠左排列。
在这里插入图片描述
2、满二叉树(Full Binary Tree): 每个节点要么没有子节点,要么有两个子节点。

在这里插入图片描述

3、平衡二叉树(Balanced Binary Tree): 任意两个叶子节点之间的高度差最多为一,即两个子树的高度差都可以是 -1,0 或 1。最常见的平衡二叉树是 AVL 树。

所有的平衡二叉树都满足二叉搜索树的条件。平衡条件确保了树的深度最大只能是 O(log n)(n为节点数量),这有助于防止树的一侧异常过长,导致操作效率下降。

一个平衡二叉树的结构示例(AVL 树):
在这里插入图片描述

在这个 AVL 树中,任何一个节点的左右子树高度差都不超过 1,这意味着它是平衡的。

在此,我们展示一个不是平衡二叉树的结构:
在这里插入图片描述

平衡二叉树是为了解决普通二叉搜索树在极端情况下会退化成链表,从而导致效率下降的问题。二叉搜索树如果不平衡,它的操作(查找、插入、删除等)的时间复杂度在最坏的情况下可以变成O(n)。而平衡二叉树通过旋转操作来保持平衡,确保操作的时间复杂度稳定在 O(log n)

4、二叉搜索树(Binary Search Tree, BST): 满足以下性质的二叉树:

  • 每个节点的左子树只包含小于当前节点的数。
  • 每个节点的右子树只包含大于当前节点的数。
  • 所有左右子树也必须是二叉搜索树。
    在这里插入图片描述

二叉树的存储结构

二叉树一般可以使用两种存储结构,一种顺序结构,一种链式结构。

二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。我们通常把这种完全二叉树使用顺序结构的数组来存储。
在这里插入图片描述
而这种存储又称作堆,这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

二叉树的链式结构

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,遇到的一般都是二叉链。

在这里插入图片描述

链表的代码展示

二叉链表的定义:

class BinaryTreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

# 示例创建二叉链表节点
root = BinaryTreeNode('A')
root.left = BinaryTreeNode('B')
root.right = BinaryTreeNode('C')

三叉链表的定义:

class TernaryTreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None
        self.parent = None

# 生成三叉链表节点并建立连接
root = TernaryTreeNode('A')
node_b = TernaryTreeNode('B')
node_c = TernaryTreeNode('C')

root.left = node_b
root.right = node_c
node_b.parent = root
node_c.parent = root

堆的基本概念和结构

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
在这里插入图片描述

堆的性质:
1、堆中某个节点的值总是不大于或不小于其父节点的值;
2、堆总是一棵完全二叉树。

堆的代码体现

小根堆展示:

import heapq

# 创建小根堆
min_heap = []
heapq.heappush(min_heap, 3)
heapq.heappush(min_heap, 1)
heapq.heappush(min_heap, 2)

# 从堆中弹出最小元素
print(heapq.heappop(min_heap))  # 输出 1
print(heapq.heappop(min_heap))  # 输出 2
print(heapq.heappop(min_heap))  # 输出 3

大根堆展示:

import heapq

# 创建大根堆的辅助函数
def heappush_max(heap, item):
    heapq.heappush(heap, -item)

def heappop_max(heap):
    return -heapq.heappop(heap)

# 创建大根堆
max_heap = []
heappush_max(max_heap, 3)
heappush_max(max_heap, 1)
heappush_max(max_heap, 2)

# 从堆中弹出最大元素
print(heappop_max(max_heap))  # 输出 3
print(heappop_max(max_heap))  # 输出 2
print(heappop_max(max_heap))  # 输出 1

在这两个例子中,小根堆可以直接通过 heapq 提供的函数 heappush 和 heappop 来操作,而大根堆则通过取反的方式使用了相同的函数。

当想将元素添加到大根堆时,可以使用 heappush_max 函数;并且当想从大根堆中弹出最大元素时,可以使用 heappop_max 函数。通过取反操作,能够保持堆的性质而适用于 heapq的小根堆实现。

二叉树生成

生成二叉树通常是指创建二叉树的过程。二叉树可以通过多种方式生成,例如:递归地构建、从数组构建、通过用户输入构建等。二叉树的生成通常包括初始化根节点以及递归或迭代地添加子节点至适当位置。
在这里插入图片描述

二叉树遍历

遍历二叉树是指按照某种顺序访问二叉树中的每一个节点,确保每个节点被访问一次。通常有以下几种遍历方法:

  1. 前序遍历(Pre-order Traversal):

    • 访问根节点
    • 遍历左子树
    • 遍历右子树
    • 可用于打印树结构,复制树结构等。
  2. 中序遍历(In-order Traversal):

    • 遍历左子树
    • 访问根节点
    • 遍历右子树
    • 对于二叉搜索树(BST),这种遍历方式会按照节点的升序排列访问它们。
  3. 后序遍历(Post-order Traversal):

    • 遍历左子树
    • 遍历右子树
    • 访问根节点
    • 常用于删除或释放树中的节点,因为它确保节点在其子节点被访问后才被访问。
  4. 层序遍历(Level-order Traversal):

    • 按照树的层级从上至下访问节点
    • 通常使用队列来辅助实现
    • 也被称为广度优先搜索(BFS, Breadth-First Search)

每种遍历方式都有其特定的用途,并且可以递归或迭代地实现。访问节点的具体操作可以根据需要进行变化,例如打印节点值、计算树的深度,或者累加节点值等。

四种不同遍历方式——代码展示

我们通过0-9这10个数字进行展示说明:

# 二叉树的节点类
class btree_node():
    # 初始化节点,每个节点有三个属性:节点值、节点左子树、节点右子树
    def __init__(self,val):
        #节点幅值
        self.data=val
        # 节点左子树
        self.left=None
        # 节点右子树
        self.right=None

# 定义二叉树类
class binary_Tree():
    def __init__(self):
        self.tree_struct=None

    # 增加节点,这个函数也是建树的过程
    def add_node(self,new_node):
        # 将树的结构放到列表中
        list_nodes=[self.tree_struct]
        # 如果是孔数,直接在书上加上这个节点
        if self.tree_struct==None:
            # 在空树上加上节点
            self.tree_struct= new_node
            return
        #在循环前list_nodes只有一棵树,在循环中间list_nodes中追加了这棵树的子树。
        while len(list_nodes)>0:
            # 从列表中弹出提排在最前面的树
            cur_node=list_nodes.pop(0)
            # 如果树的左子树为空,把该节点当作该树的左子树
            if cur_node.left==None:
                # 让新结点成为树的左子树
                cur_node.left=new_node
                return
            else:
                # 如果树的左子树不为空,则把这棵树的左子树追加到列表后面
                list_nodes.append(cur_node.left)
                # 如果树的右子树为空,把新结点当作该数的右子树
            if cur_node.right==None:
                # 让新结点成为树的右子树
                cur_node.right=new_node
                return
            else:
                # 如果树的右子树不为空,把这个树的右子树加到列表后面
                list_nodes.append(cur_node.right)

    # 广度遍历,从树的顶层,一层层遍历,每一层从左到右取各结点数值
    def breadth_travel(self):
        # 如果是空树直接返回
        if self.tree_struct == None:
            return
        # 将树的结构放到列表中
        list_nodes = [self.tree_struct]
        # 以下循环主要流程是:
        # 每次从列表中弹出最前面的一个树(第一次弹出的是整个树,以后是弹出的是子树),
        # 打印这个树根结点的值,
        # 先看一下这个树的左孩子(左子树)是否为空,不为空,
        # 把该树的左孩子(左子树)追加到列表后面,
        # 再看一下这个树的右孩子(右子树)是否为空,
        # 不为空,把该树的右孩子(右子树)追加到列表后面,
        # 进入下一次循环,再次从列表最前面弹出一个树(子树)
        while len(list_nodes) > 0:
            cur_node = list_nodes.pop(0)
            print(cur_node.data, end='')
            if cur_node.left != None:
                list_nodes.append(cur_node.left)
            if cur_node.right != None:
                list_nodes.append(cur_node.right)

    # 先序遍历,按照根节点、左子树、右子树的顺序访问二叉树
    # 第1步:访问根节点(或子树根节点);
    # 第2步:递归调用本函数遍历左子树;
    # 第3步:递归调用本函数遍历右子树。
    def pre_travel(self, tree_node):
        # 如果树为空直接返回
        if tree_node == None:
            return
        # 打印出当前树根结点的值
        print(tree_node.data, end='')
        # 对左子树递归调用
        self.pre_travel(tree_node.left)
        # 对右子树递归调用
        self.pre_travel(tree_node.right)

    # 中序遍历:按照左子树、根节点、右子树的顺序访问
    # 第1步:递归调用本函数遍历左子树;
    # 第2步:访问根节点(或子树根节点);
    # 第3步:递归调用本函数遍历右子树。
    def midd_travle(self, tree_node):
        # 如果树为空直接返回
        if tree_node == None:
            return
        # 对左子树递归调用
        self.midd_travle(tree_node.left)
        # 打印出当前树根结点的值
        print(tree_node.data, end='')
        # 对右子树递归调用
        self.midd_travle(tree_node.right)

    # 后序遍历:按照左子树、右子树、根节点的顺序访问
    # 第1步:递归调用本函数遍历左子树;
    # 第2步:递归调用本函数遍历右子树;
    # 第3步:访问根节点(或子树根节点)。
    def back_travel(self, tree_node):
        # 如果树为空直接返回
        if tree_node == None:
            return
        # 对左子树递归调用
        self.back_travel(tree_node.left)
        # 对右子树递归调用
        self.back_travel(tree_node.right)
        print(tree_node.data, end='')


# 主程序main
if __name__ == "__main__":
    tree = binary_Tree()
    for i in range(10):
        new_node = btree_node(i)
        tree.add_node(new_node)
    print('广度遍历结果:', end='')
    tree.breadth_travel()
    print('')
    print('前序遍历结果:', end='')
    tree.pre_travel(tree.tree_struct)
    print('')
    print('中序遍历结果:', end='')
    tree.midd_travle(tree.tree_struct)
    print('')
    print('后序遍历结果:', end='')
    tree.back_travel(tree.tree_struct)

运行结果展示:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1648685.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统编程 —— 进程概念,环境变量,虚拟地址空间总结(收藏向)

目录 一,什么是进程,有什么用? 1.1 关于进程 1.2 进程的重要特性 二,进程的描述:PCB结构体 2.1 关于PCB 2.2 task_struct 三,进程pid与ppid 3.1 pid是什么?怎么查看? 3.2 系…

51单片机入门:蜂鸣器

蜂鸣器介绍 蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号。 蜂鸣器的种类 1、从结构上:压电式蜂鸣器和电磁式蜂鸣器。 压电式蜂鸣器:通过压电陶瓷的压电效应原理工作的。当加有交变电压时&#xf…

AMEYA360详解:蔡司利用纳米探针技术探索半导体微观电学性能

半导体器件尺寸不断缩小和复杂度增加,纳米探针(Nanoprobing)技术成为解决微观电学问题和优化器件性能的重要工具,成为半导体失效分析流程中越来越重要的一环。 随着功率半导体的快速发展,其厂商也开始密切关注纳米探针技术在PN结特性分析和掺…

什么是光伏发电?什么是分布式光伏系统?

一、光伏发电 光伏发电,作为一种可再生能源利用技术,其核心原理基于半导体的光生伏特效应。简而言之,光伏发电就是将太阳能直接转换为电能的过程。它由三个主要部分组成:太阳电池板(组件)、控制器和逆变器…

LMdeploy推理实践

在inter-studio平台上,下载模型,体验lmdeploy 下载模型 这里是因为平台上已经有了internlm2模型,所以建立一个符号链接指向它,没有重新下载 ln -s /root/share/new_models/Shanghai_AI_Laboratory/internlm2-chat-1_8b /root/如…

TC8002D 是一颗带关断模式的音频功放IC

一、一般概述 TC8002D是一颗带关断模式的音频功放IC。在5V输入电压下工作时,负载(3Ω)上的平均功率 为3 W,且失真度不超过10%。而对于手提设备而言,当VDD作用于关断端时,TC8002D将会进入关断模式,此时的功耗极…

【UE】利用物理学放置模型(以堆积石块为例)

目录 效果 步骤 一、准备工作 二、设置石块碰撞 三、绘制石块 效果 步骤 一、准备工作 1. 在虚幻商城中安装“Physical Layout Tool”插件 2. 在虚幻编辑器中勾选插件“Physical Layout”插件 3. 在Quixel Bridge中将我们所需要的石块资产添加到项目中 这里我们导入…

如何与精益生产咨询公司合作,确保项目的成功?

随着竞争的白热化,企业为了提升生产效率和降低成本,纷纷寻求精益生产咨询公司的帮助。然而,与咨询公司合作并不是一蹴而就的事情,需要双方共同努力,才能确保项目的成功。那么,如何与精益生产咨询公司合作&a…

个股期权是什么期权?个股期权什么时候推出?

今天期权懂带你了解个股期权是什么期权?个股期权什么时候推出?期权也称选择权,是指期权的买方有权在约定的期限内,按照事先确定的价格,买入或卖出一定数量某种特定商品或金融指标的权利。 个股期权是什么期权&#xff…

uni-app安卓本地打包个推图标配置

如果什么都不配置,默认的就是个推小鲸鱼图标 默认效果 配置成功效果 个推图标配置 新建目录 drawable-hdpi、drawable-ldpi、drawable-mdpi、drawable-xhdpi、drawable-xxhdpi、drawable-xxxhdpi 目录中存放图标 每个目录中存放对应大小的图标,大图…

Lora训练笔记1——快速上手

准备工具 AKI大佬的整合包,一键解压即可。 度盘链接 提取码:p8uy 图片预处理 图片预处理:以一定规则裁剪原始的训练素材图片,并进行打标处理。 新建两个文件夹 input:存放原始图片的文件夹 preprocess-output:…

一文带你了解 Oracle 23ai 新特性 Vector 的基础用法

Oracle Database 23ai 来了,虽然目前只是云上可商用,但是 OP 有 FREE 版本可以进行开发。 本文将介绍 Oracle 23ai 的新特性之一: AI 向量搜索,的部分内容。 向量数据类型 23ai 新增向量数据类型,可以用于表示一系列的…

​Web服务器

代码: WebServer 介绍 HTTP层 POST请求一般会包含Content-Length字段, 告诉服务器请求主体的长度GET请求一般不会包含Content-Length字段, 它一般不含请求正文POST响应: Content-Type字段, 根据请求资源的后缀填写 编写 1.读取请求与分析请求 2.根据不同请求方法构建不同的响…

前后端功能实现——查询所有

目录 1、需求 2、步骤 1)创建模块 引入坐标 2)创建结构 实现三层架构 3)创建表 brand 4)创建实体类 Brand 5)创建MyBatis配置文件 6)创建映射文件 7)创建工具类 SqlSessionFactoryUti…

5月11日,MongoDB技术沙龙·杭州站来了,本周六不见不散!

在数字化转型的新时代,MongoDB凭借灵活的文档模型、高效的数据处理能力和出色的扩展性,成为广受企业和开发者喜爱的数据库。本次活动将聚焦如何利用MongoDB简化开发工作,并分享阿里云和NineData的解决方案,更有重磅嘉宾分享实践经…

接入大量设备后,视频汇聚系统EasyCVR安防监控视频融合平台是如何实现负载均衡的?

一、负载均衡 随着技术的不断进步和监控需求的日益增长,企业视频监控系统的规模也在不断扩大,接入大量监控设备已成为一项常态化的挑战。为确保企业能够有效应对这一挑战,视频汇聚系统EasyCVR视频融合平台凭借其卓越的高并发处理能力&#x…

快速掌握Redis优化要点,告别性能瓶颈!

大家好!我是小米,今天和大家分享一下在Redis中如何进行优化,以提升系统性能。Redis作为一种流行的内存数据库,因其高性能、高可用和数据持久性而受到广泛应用。然而,在实际应用中,我们仍需对Redis进行优化,以满足各种业务需求。接下来,我将从读写方式、KV size、Key数量…

使用IIS部署Vue项目

前提 使用IIS部署Vue项目,后端必须跨域,不要在Vue中用proxy跨域,那个只在dev环境中有用! IIS安装,不用全部打勾,有些他默认就是方块 ■ 选择性安装的,就维持原样就可以。 添加网站配置 右键…

LEETCODE LCR 041. 数据流中的移动平均值

class MovingAverage:def __init__(self, size: int):"""Initialize your data structure here."""self.sizesize1self.front0self.rear0self.queue[None for _ in range(size1)]self.sum0def next(self, val: int) -> float:# 满了if (self.…

深度学习之基于YOLOv5草莓成熟度目标检测系统

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 草莓作为一种广受欢迎的水果,其成熟度的判断对于保证草莓的品质和口感至关重要。然…