数据结构--》解锁数据结构中树与二叉树的奥秘(一)

news2024/12/23 8:58:40

        数据结构中的树与二叉树,是在建立非线性数据结构方面极为重要的两个概念。它们不仅能够模拟出生活中各种实际问题的复杂关系,还常被用于实现搜索、排序、查找等算法,甚至成为一些大型软件和系统中的基础设施。

        无论你是初学者还是进阶者,本文将为你提供简单易懂、实用可行的知识点,帮助你更好地掌握树和二叉树在数据结构和算法中的重要性,进而提升算法解题的能力。接下来让我们开启数据结构与算法的奇妙之旅吧。

目录

树和森林的概念

树的常考性质

二叉树的定义及其性质

二叉树的表示

二叉树遍历


树和森林的概念

树的定义:树是一种非线性的数据结构,它由节点(node)和边(edge)组成。树的基本概念是以层次结构来组织和表示数据。

在树中,有一个特殊的节点被称为根节点(root),它是树的顶层节点,所有其他节点都直接或间接地与根节点相连。除了根节点外,每个节点可以有零个或多个子节点(child),子节点又可以有自己的子节点,形成了树的分支结构。没有子节点的节点被称为叶节点(leaf)或叶子节点,它们位于树的最底层。节点之间的连接称为边,边描述了节点之间的关系。每个节点可以有零条到多条边连接到其子节点。任意两个节点之间都存在唯一的路径,通过路径可以从一个节点到达另一个节点。

树的结构具有以下特点

  1. 一个树可以由零个或多个节点组成。
  2. 有且只有一个根节点,它是树的起点。
  3. 每个节点可以有零个或多个子节点。
  4. 节点之间通过边相连,形成层次结构。
  5. 每个节点除了根节点外,都有且只有一个父节点。

树的基本术语

结点之间的关系描述

        根节点(Root Node):树的顶层节点称为根节点。根节点是树的起点,它没有父节点,其他所有节点都直接或间接地与根节点相连。

        祖先节点(Ancestor Node):对于一个节点,它的所有上级节点(包括父节点、父节点的父节点等等)都被称为该节点的祖先节点。

        子孙节点(Descendant Node):对于一个节点,它的所有下级节点(包括子节点、子节点的子节点等等)都被称为该节点的子孙节点。

        父节点(Parent Node):一个节点的直接上一级节点称为其父节点。每个节点都可以有零个或多个子节点,但只能有一个父节点(除了根节点)。

        子节点(Child Node):一个节点直接连接的下一级节点称为其子节点。一个节点可以有零个或多个子节点。

        兄弟节点(Sibling Node):具有相同父节点的节点称为兄弟节点。兄弟节点在同一层级上。

        叶节点(Leaf Node):也称为叶子节点,是没有子节点的节点,位于树的最底层。

        层级(Level):根节点在第一层,其直接子节点在第二层,以此类推。一个节点所在的层级数即为该节点的层级。

结点、树的属性描述

        节点值(Node Value):每个节点都可以携带一个值或数据,表示该节点所代表的实际含义或信息。

        节点深度(Node Depth):节点深度指的是该节点到根节点的路径长度,即从根节点到该节点所经过的边的数量。根节点的深度为0。

        节点高度(Node Height):节点高度指的是该节点到其最远叶节点的路径长度,即从该节点到达最远叶节点所经过的边的数量。叶节点的高度为0。

        子树(Subtree):对于一个树中的节点,可以以该节点为根构成的子树称为该节点的子树。

        树的大小(Tree Size):指的是树中包含的所有节点的总数。

        树的高度(Tree Height):指的是树中任意节点的高度的最大值。也可以理解为从根节点到最远叶节点的路径长度的最大值。

有序树、无序树

        有序树(Ordered Tree):有序树是指树中的子节点之间存在明确的顺序关系。在有序树中,每个子节点都有一个明确定义的位置,在遍历和表示树的时候需要按照顺序来考虑。例如,家谱树中的兄弟姐妹一般按照他们出生的先后顺序排列。

        无序树(Unordered Tree):无序树是指树中的子节点之间没有明确的顺序关系。在无序树中,所有子节点都是平等的,没有先后之分。例如,文件系统中的目录结构就是一种无序树,其中的各个子目录之间没有特定的顺序。

        有序树和无序树的区别在于子节点的排列方式。在有序树中,子节点的顺序很重要,会影响到树的结构和含义;而在无序树中,子节点的顺序并不重要,只需要知道它们是该节点的子节点即可。

森林

森林(Forest)是指由多棵树(Tree)组成的集合。简单来说,森林可以看作是多个独立的树的集合。

森林的特点在于其中的树之间是相互独立的,彼此之间没有直接的连接或关系。每棵树都可以独立地进行遍历和操作。

需要注意的是,森林和树的层次结构是不同的概念。树是一种层次结构,它具有唯一的根节点和从根节点到其他节点的确定路径;而森林则是多个独立的树的集合,在森林中任意两棵树之间没有直接的联系。

树的抽象数据类型

树的抽象数据类型定义了对树进行操作的基本操作集合,包括以下常见操作:

1)创建树:创建一个空的树数据结构。

2)插入节点:在树中插入一个新节点,并建立节点之间的关系。

3)删除节点:从树中删除指定的节点,并调整节点之间的关系。

4)遍历树:按照特定的顺序访问树中的节点,例如先序遍历、中序遍历、后序遍历等。

5)查找节点:在树中查找指定的节点。

6)获取树的属性:获取树的高度、节点个数、根节点等属性信息。

树的抽象数据类型并不关注具体的实现方式,而是定义了可以对树进行的操作以及这些操作的预期行为。

回顾重点,其主要内容整理成如下内容:  

树的常考性质

常见考点1:结点数 = 总度数 + 1

常见考点2:度为m的树、m叉树的区别

常见考点3:度为m的树第i层至多有  m^{i-1} 个结点 (i \geq 1),同理m叉树第i层至多有 m^{i-1} 个结点 (i \geq 1):

常见考点4:高度为h的m叉树至多有 \frac{m^{h}-1}{m-1} 个结点。

常见考点5:高度为h的m叉树至少有h个结点。高度为h度为m的树至少有h+m-1个结点。

常见考点6:具有n个结点的m叉树的最小高度为 log_m(n(m-1)+1)

高度最小的情况——所有结点都有m个孩子:

回顾重点,其主要内容整理成如下内容:   

二叉树的定义及其性质

二叉树定义

二叉树是n(n\geqslant0)个结点的有限集合,其有以下两种情况:

1)为空二叉树,即 n = 0 的时候

2)由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一颗二叉树。

特点:每个结点至多只有两颗子树;左右子树不能颠倒。

二叉树的五种状态

几个特殊的二叉树

满二叉树:所有叶节点都在同一层,并且所有非叶节点都有两个子节点的二叉树称为满二叉树。

完全二叉树:除了最后一层节点之外,所有节点都拥有两个子节点,并且最后一层的节点都向左对齐的二叉树称为完全二叉树。

二叉排序树:(比如找关键字为60的结点)

平衡二叉树:树上任一结点的左子树和右子树的深度之差不超过1。

回顾重点,其主要内容整理成如下内容:   

二叉树的常考性质

常见考点1:设非空二叉树中度为0、1和2的结点个数分别为 n_0,n_1,n_2 ,则 n_0 = n_2 + 1

叶子结点比二分支结点多一个

常见考点2:二叉树第i层至多有 2^{i-1} 个结点(i\geqslant1);m叉树第i层至多有 m^{i-1} 个结点(i\geqslant1)

常见考点3:高度为h的二叉树至多有 2^{h}-1 个结点(满二叉树)

高度为h的m叉树至多有 \frac{m^{h}-1}{m-1} 个结点

完全二叉树的常考性质

常见考点1:具有n个(n>0)结点的完全二叉树的高度h为 log_2(n+1)log_2n + 1

常见考点2:对于完全二叉树,可以由结点数 n 推出度为 0、1和2的结点个数为n_0n_1n_2

回顾重点,其主要内容整理成如下内容:    

二叉树的表示

在数据结构中,二叉树可以通过数组表示和链表存储表示两种方式来实现。下面分别对这两种表示方式进行简述:

数组表示

数组表示是将二叉树的节点按照某种方式存储在一个一维数组中。一般情况下,数组可以按照层次遍历的顺序存储二叉树的节点。假设根节点存储在数组下标为0的位置,那么对于任意一个节点的索引 i,其左子节点的索引为 2i+1,右子节点的索引为 2i+2。如果某个位置为空,可以使用特定的空值表示。

数组表示的优点

数组表示的优点是存储简单,通过数组索引可以快速访问到任意节点。缺点是当二叉树的形状发生改变时,需要重新调整数组的大小,可能涉及到大量的元素移动。

#include <stdio.h>
#include <stdlib.h>

// 二叉树节点结构体
struct TreeNode {
    int val;
    struct TreeNode* left;
    struct TreeNode* right;
};

// 构建二叉树的数组表示
struct TreeNode* build_binary_tree(int arr[], int size) {
    struct TreeNode** tree = malloc(sizeof(struct TreeNode*) * size);
    
    for (int i = 0; i < size; i++) {
        if (arr[i] != -1) {
            struct TreeNode* node = malloc(sizeof(struct TreeNode));
            node->val = arr[i];
            node->left = NULL;
            node->right = NULL;
            tree[i] = node;
        } else {
            tree[i] = NULL;
        }
    }
    
    for (int i = 0; i < size; i++) {
        if (tree[i] != NULL) {
            if (2 * i + 1 < size)
                tree[i]->left = tree[2 * i + 1];
            if (2 * i + 2 < size)
                tree[i]->right = tree[2 * i + 2];
        }
    }
    
    struct TreeNode* root = tree[0];
    free(tree);
    
    return root;
}

// 测试代码
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7};
    int size = sizeof(arr) / sizeof(arr[0]);
    
    struct TreeNode* root = build_binary_tree(arr, size);
    
    // 输出测试结果
    printf("root: %d\n", root->val);
    printf("left child of root: %d\n", root->left->val);
    printf("right child of root: %d\n", root->right->val);
    
    return 0;
}

链表存储表示

链表存储表示是使用链表的方式来表示二叉树。每个节点包含一个指向其父节点的指针,一个指向其左子节点的指针,一个指向其右子节点的指针。

链表存储表示的优点

链表存储表示的优点是可以灵活地插入、删除节点而不需要移动其他节点,适用于频繁变化的二叉树结构。缺点是访问某个节点需要从根节点开始遍历,效率相对较低。

#include <stdio.h>
#include <stdlib.h>

// 二叉树节点结构体
struct TreeNode {
    int val;
    struct TreeNode* left;
    struct TreeNode* right;
};

// 构建二叉树的链表存储表示
struct TreeNode* build_binary_tree(int arr[], int size) {
    if (size == 0) {
        return NULL;
    }
    
    struct TreeNode* root = malloc(sizeof(struct TreeNode));
    root->val = arr[0];
    root->left = NULL;
    root->right = NULL;
    
    struct TreeNode** queue = malloc(sizeof(struct TreeNode*) * size);
    int front = 0;
    int rear = 0;
    queue[rear++] = root;
    
    int i = 1;
    while (i < size) {
        struct TreeNode* node = queue[front++];
        
        // 处理左子节点
        if (arr[i] != -1) {
            node->left = malloc(sizeof(struct TreeNode));
            node->left->val = arr[i];
            node->left->left = NULL;
            node->left->right = NULL;
            queue[rear++] = node->left;
        }
        i++;
        
        // 处理右子节点
        if (i < size && arr[i] != -1) {
            node->right = malloc(sizeof(struct TreeNode));
            node->right->val = arr[i];
            node->right->left = NULL;
            node->right->right = NULL;
            queue[rear++] = node->right;
        }
        i++;
    }
    
    free(queue);
    
    return root;
}

// 测试代码
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7};
    int size = sizeof(arr) / sizeof(arr[0]);
    
    struct TreeNode* root = build_binary_tree(arr, size);
    
    // 输出测试结果
    printf("root: %d\n", root->val);
    printf("left child of root: %d\n", root->left->val);
    printf("right child of root: %d\n", root->right->val);
    
    return 0;
}

二叉树遍历

遍历:按照某种次序把所有结点都访问一遍。根据二叉树的递归特性要么是个空二叉树,要么就是由“根结点+左子树+右子树”组成的二叉树

根据二叉树的三种遍历规则,相关的具体案例如下:

再进行具体的练习一遍:

回顾重点,其主要内容整理成如下内容:

二叉树的层序(次)遍历

其相应的代码实现如下:

由遍历序列构造二叉树

回顾重点,其主要内容整理成如下内容: 

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

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

相关文章

三角函数和角公式

该文章对三角函数和角公式做了多种证明&#xff1a;https://mp.weixin.qq.com/s?__bizMzI4ODYwNTM3Ng&mid2247484178&idx1&sn1f6e04c50ae30b63198201db3d9a4f05&chksmec3a96bddb4d1fab4baf8188ca6ba60d8d4364be4f08dc53e13b2e4cdfcec4fdb43928108001&scen…

10_9C++

X-mind #include <iostream> using namespace std; class Per { private:string name;int age;float *height;float *weight; public:Per()//无参构造函数{cout << "无参构造函数" << endl;}Per(string name,int age,float *height,float *weight)…

MySQL——单表与多表查询练习

MySQL 一、练习一二、练习二 一、练习一 这里首先将素材创建完毕&#xff0c;首先创建一个数据库并使用&#xff0c;这里我创建的数据库名为worker&#xff1a; 紧接着我们创建数据库表并创建表结构&#xff1a; 查看表结构&#xff1a; 接着我们导入数据&#xff1a; 这…

【LeetCode】剑指 Offer Ⅱ 第6章:栈(6道题) -- Java Version

题库链接&#xff1a;https://leetcode.cn/problem-list/e8X3pBZi/ 类型题目解决方案栈的应用剑指 Offer II 036. 后缀表达式模拟 栈 ⭐剑指 Offer II 037. 小行星碰撞分类讨论 栈 ⭐单调栈剑指 Offer II 038. 每日温度单调栈 ⭐剑指 Offer II 039. 直方图最大矩形面积单调栈…

Excel·VBA使用ADO合并工作簿

之前文章《ExcelVBA合并工作簿&#xff08;7&#xff0c;合并子文件夹同名工作簿中同名工作表&#xff0c;纵向汇总数据&#xff09;》处理合并工作簿问题&#xff0c;代码运行速度比较慢 而《ExcelVBA使用ADO读取工作簿工作表数据》读取数据非常快&#xff0c;那么是否可以使用…

vue3+elementui实现表格样式可配置

后端接口传回的数据格式如下图 需要依靠后端传回的数据控制表格样式 实现代码 <!-- 可视化配置-表格 --> <template><div class"tabulation_main" ref"myDiv"><!-- 尝试过在mounted中使用this.$refs.myDiv.offsetHeight,获取父元素…

Redis安装及key、string操作

安装 在官网下载的数据包上传到Linux家目录 Install Redis from Source | Redis wget https://download.redis.io/redis-stable.tar.gz tar -xzvf redis-stable.tar.gz cd redis-stable make 编译后出现以下提示后输入make install 出现以下提示则安装成功 输入redis-sever启…

扩展屏幕,副屏幕的使用与设置

1、设置扩展屏模式 按快捷键 win p 选择 扩展 模式 2、设置屏幕的方向 打开电脑的设置页面 选择 系统 点击 屏幕&#xff0c;然后 拖动两个屏幕的位置即可

Computer Architecture Subtitle:Engineering And Technology

原文链接&#xff1a;https://www.cs.umd.edu/~meesh/411/CA-online/index.html

FBZP 维护支持程序 创建国家付款方式

今天在扩充供应商时报了一个错误&#xff1a; 收付方式 I 没有为国家 HK 定义。 原因是香港没有I的支付方式&#xff0c;需要为HK增加一下。方法如下&#xff1a; SPRO 路径&#xff1a;财务会计&#xff08;新&#xff09;-->>应收帐目和应付帐目-->>业务交易--&…

ELementUI之CURD及表单验证

一.CURD 1.后端CURD实现 RequestMapping("/addBook")ResponseBodypublic JsonResponseBody<?> addBook(Book book){try {bookService.insert(book);return new JsonResponseBody<>("新增书本成功",true,0,null);} catch (Exception e) {e.p…

基于Winform的UDP通信

1、文件结构 2、UdpReceiver.cs using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks;namespace UDPTest.Udp {public class UdpStateEventArgs : EventArgs…

【C/C++】结构体内存分配问题

规则1&#xff1a;以多少个字节为单位开辟内存 就是说&#xff0c;该结构体最终所占字节大小&#xff0c;是这个单位的整数倍 给结构体变量分配内存的时候&#xff0c;会去结构体变量中找基本类型的成员 哪个基本类型的成员占字节数多&#xff0c;就以它大大小为单位开辟内存 …

数据产品读书笔记——数据产品经理和其他角色的关系

&#x1f34a;上一节我们初步对数据产品经理的角色有了初步的了解&#xff0c;今天我们继续学习数据产品经理与其他角色之间的关系。上一期的内容如下&#x1f447;: 链接: 数据产品读书笔记——认识数据产品经理 &#x1f340;当我们处在一个组织中&#xff0c;就一定会有与…

leetcode:2427. 公因子的数目(python3解法)

难度&#xff1a;简单 给你两个正整数 a 和 b &#xff0c;返回 a 和 b 的 公 因子的数目。 如果 x 可以同时整除 a 和 b &#xff0c;则认为 x 是 a 和 b 的一个 公因子 。 示例 1&#xff1a; 输入&#xff1a;a 12, b 6 输出&#xff1a;4 解释&#xff1a;12 和 6 的公因…

大模型rlhf 相关博客

想学习第一篇博客: https://huggingface.co/blog/zh/rlhf RLHF 技术分解 RLHF 是一项涉及多个模型和不同训练阶段的复杂概念&#xff0c;这里我们按三个步骤分解&#xff1a; 预训练一个语言模型 (LM) &#xff1b;聚合问答数据并训练一个奖励模型 (Reward Model&#xff0c;RM…

数据结构和算法(10):B-树

B-树&#xff1a;大数据 现代电子计算机发展速度空前&#xff0c;就存储能力而言&#xff0c;情况似乎也是如此&#xff1a;如今容量以TB计的硬盘也不过数百元&#xff0c;内存的常规容量也已达到GB量级。 然而从实际应用的需求来看&#xff0c;问题规模的膨胀却远远快于存储能…

10.9作业

设计一个Per类&#xff0c;类中包含私有成员:姓名、年龄、指针成员身高、体重&#xff0c;再设计一个Stu类&#xff0c;类中包含私有成员:成绩、Per类对象p1&#xff0c;设计这两个类的构造函数、析构函数和拷贝构造函数。 #include <iostream>using namespace std;clas…

Java实现哈希表

1.哈希表定义 哈希表&#xff08;hash table&#xff0c;也叫散列表&#xff09;&#xff0c;是根据关键码值&#xff08;key value&#xff09;而直接进行访问的数据结构。也就是说&#xff0c;它通过把关键码值映射到表中一个位置来访问记录&#xff0c;以加快查找的速度。这…

【深度学习实验】卷积神经网络(七):实现深度残差神经网络ResNet

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入必要的工具包 1. Residual&#xff08;残差连接&#xff09; __init__&#xff08;初始化&#xff09; forward&#xff08;前向传播&#xff09; 2. resnet_block&#xff08;残…