5.C_数据结构_树

概述

树的逻辑结构:

树中任何节点都可以有0个或多个直接后继节点,但最多只有一个直接前驱节点。根节点没有直接前驱节点,叶节点没有直接后继节点。 

相关名词:

  • 空树:树中没有节点
  • 节点的度数:一个节点的子树的个数
  • 树的度数:该树中节点的最大度数
  • 树叶、终端节点:度数为0的节点
  • 分支节点:度数不为0的节点
  • 内部节点:除根节点以外的分支节点
  • 路径:能够找到节点的方式,例如:A到L的路径是ABEL
  • 路径的长度:路径中的边数,边有A-B、B-E、E-L,即:长度为3
  • 节点的层数:节点的层数 = 父节点的层数+1,根节点的层数为1
  • 树的高度、深度:树中节点层数的最大值。例如:下图中深度为4
  • 祖先:路径中前面的节点为后面的祖先,例如:A是K的祖先
  • 子孙:路径中后面的节点为前面的子孙
  • 堂兄弟:层级一样但父节点不一样的节点为堂兄弟,例如:F、G
  • 兄弟:层级一样,父节点也一样的节点为兄弟,例如:E、F
  • 有序树:兄弟之间是有序的、不能交换位置的树
  • 森林:m棵互不相交的树,树去掉根节点就是森林,森林加上根节点就是树

二叉树:

二叉树是树的度数为2的有序树,即:一个父节点最多有2个子节点,且这两个节点是有顺序的。除此之外,子节点也没有指向父节点的指针。

  • 二叉树第 i 层上的节点最多有:2^(i-1)个。
  • 对于深度为k的二叉树,节点最多有:2^0 + 2^1 + ... + 2^(k-1) = 2^k -1 个
  • 满二叉树:深度为k时,有2^k-1个节点的二叉树,即每一层都是满的。
  • 完全二叉树:只有最下面两层有度数小于2的节点,且最下面的叶节点集中在最左边的位置上
  • 有n个节点的完全二叉树的深度为:log2n + 1 或 log2(n+1)

二叉树顺序存储:

编号方法是从上到下,从左到右进行编号,规定根节点的编号为1。这种方法只适用于完全二叉树,如果是不完全二叉树需要添加虚节点构成完全二叉树,这会大大增加冗余的内存使用。

假设总共有 n 个节点,当前节点的编号为 i 。则有以下结论:

  • 当编号 i ≠ 1时,有父节点,父节点的编号为 i/2
  • 当 2*i < n 时,代表有左孩子。当 2*i+1 < n 时,代表有右孩子。
  • 当 i 为奇数且不为1时,一定有左兄弟。当 i 为偶数且 i < n 时,一定有右兄弟

顺序存储的特点:

  • 二叉树的顺序存储是用数组的方式进行存储,它的空间连续。
  • 只有当二叉树为完全二叉树时,顺序存储才不会有空间浪费,否则需要先添加虚节点构成完全二叉树,再进行顺序存储。

二叉树链式存储:

由下一章详细讲解。

二叉树链式存储

1、基本内容

二叉树的链式存储是以链表的形式存储每一个节点。每一个节点都可以看成一个根,在这个根中存放了一些数据data,并且这个根中有两个树叶,即:左孩子和右孩子。具体的节点结构如下:

二叉树结点结构体声明如下:

typedef char tree_data_t;
typedef struct tree{tree_data_t data;     //root中存放的数据struct tree* left;    //左孩子struct tree* right;   //右孩子
}bitree;

二叉树代码的文件构成:

  • bitree.h:数据结构的定义、运算函数接口
  • bitree.c:运算函数接口的实现
  • linkqueue.c:分层遍历二叉树会用到队列
  • test.c:使用数据结构实现的应用功能代码

2、二叉树遍历代码实现

2.1 先序、中序、后序

二叉树的遍历方法:

  • 先序遍历:先访问树根,再访问左子树,最后访问右子树
  • 中序遍历:先访问左子树,再访问树根,最后访问右子树
  • 后序遍历:先访问左子树,再访问右子树,最后访问树根

可以看到这是一个递归问题,以后序遍历为例,每一个结点都是先访问左子树,再访问右子树,最后访问树根。例如下图,从A出发,先访左子树,左子树为B。之后再访问B的左子树,为空;之后访问B的右子树,为C;之后访问C的左子树为D;之后访问D的左子树,为空;之后访问D的右子树,为空;之后访问D的根,为D。这时才访问到第一个结点D。可以看到当结点为A时,是先访问左子树,访问到后,就开始第二次重复的操作。对B结点同样是先访问左子树。这个就是递归。

先序遍历代码编写思路:

从上述分析可以看出,对于二叉树的遍历,我们只需要专注与一个结点该如何遍历,之后所有结点遍历的方式都是一样的。

递归函数的前提是有一个退出条件,当我们发现需要继续往下遍历的结点为空时,这就不再需要遍历,最终递归退出。

具体代码实现如下:

/** preorder:先序遍历* param root:根指针* */
void preorder(bitree* root){//停止条件:当根节点为空时,不再访问if(root == NULL){return;}printf("%c",root->data);//访问根preorder(root->left);   //左结点以同样的方式访问preorder(root->right);  //右结点以同样的方式访问
}

中序遍历与后续遍历代码:

中序遍历与后续遍历的思路与先序遍历完全一致,都是先规定好递归退出条件,之后递归的处理每一个结点。

具体代码实现如下:

/** inorder:中序遍历* param root:根指针* */
void inorder(bitree* root){//停止条件:当根节点为空时,不再访问if(root == NULL){return;}inorder(root->left);    //左printf("%c",root->data);//根inorder(root->right);   //右
}/** postorder:后序遍历* param root:根指针* */
void postorder(bitree* root){//停止条件:当根节点为空时,不再访问if(root == NULL){return;}postorder(root->left);   //左postorder(root->right);  //右printf("%c",root->data); //根
}

2.2 层次遍历

二叉树的层次遍历需要用到队列。我们首先从队列中取出一个结点,这就代表访问了这个结点;之后将这个结点左右孩子非空,哪就入队列。之后循环操作,继续取出一个结点,继续将结点的左右孩子入队列。

具体代码实现如下:

/** layorder:层级遍历* param root:根指针* */
void layerorder(bitree* root){bitree* tmp = NULL;linkqueue* pQueue = NULL;if(root == NULL){return;}//1.建立队列pQueue = queue_create();if(pQueue == NULL){return;}//2.初始化队列,将根入队enter_queue(pQueue,root);//3.开始层级遍历while(out_queue(pQueue,&tmp) != -1){if(tmp!=NULL){printf("%c",tmp->data);enter_queue(pQueue,tmp->left);enter_queue(pQueue,tmp->right);}}puts("");
}

3、二叉树创建代码实现

二叉树的创建代码,就是按照遍历的逻辑依次写入数据,比如写入的数据为char型,那就需要规定一个停止向下遍历的字符,以下代码中规定字符为' # '

具体代码实现如下:

/** bitree_creat:一次性创建出一个树* @ret  根的指针* */
bitree* bitree_create(void){char data = '#';bitree* root = NULL;//停止条件:规定data=#时代表结点不存在scanf("%c",&data);if(data == '#'){return NULL;}//结点存在,申请根结点空间root = (bitree*)malloc(sizeof(bitree));if(root == NULL){printf("malloc err\n");return NULL;}root->data = data;//初始化左右孩子root->left = bitree_create();root->right = bitree_create();return root;
}

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

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

相关文章

【纯小白论文代码带读】医学图像分割MASDF-Net(问题产生及解决)

论文链接&#xff1a;https://www.semanticscholar.org/paper/MASDF-Net%3A-A-Multi-Attention-Codec-Network-with-and-Fu-Deng/6ab609eb93dfd12596032174ca9603712f5c050a 代码链接&#xff1a;https://github.com/Rayicer/TransFuse 初见面代码&#xff1a; Q&am…

【字符函数】strcpy函数(字符串复制函数)+strcat函数(字符串追加)+strcmp函数(字符串比较)【笔记】

1.复制函数--------------strcpy函数 函数使用 char*strcpy&#xff08;char* destination, const char* source&#xff09; strcpy函数用于拷贝字符串&#xff0c;即将一个字符串中的内容拷贝到另一个字符串中&#xff08;会覆盖原字符串内容&#xff09;。它的参数是两个指…

大数据新视界 --大数据大厂之Kubernetes与大数据:容器化部署的最佳实践

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Java键盘输入语句

编程输入语句 1.介绍:在编程中&#xff0c;需要接受用户输入的数据&#xff0c;就可以使用键盘输入语句来获取。 2.步骤&#xff1a; 1&#xff09;导入该类的所在包&#xff0c;java.util.* 2)创建该类对象&#xff08;声明变量&#xff09; 3&#xff09;调用里面的功能 3…

面试官:什么是CAS?存在什么问题?

大家好&#xff0c;我是大明哥&#xff0c;一个专注「死磕 Java」系列创作的硬核程序员。 回答 CAS&#xff0c;Compare And Swap&#xff0c;即比较并交换&#xff0c;它一种无锁编程技术的核心机制。其工作方式分为两步&#xff1a; 比较&#xff1a;它首先会比较内存中的某…

C++的初阶模板和STL

C的初阶模板和STL 回顾之前的内存管理&#xff0c;我们还要补充一个概念&#xff1a;内存池 也就是定位new会用到的场景&#xff0c;内存池只会去开辟空间。 申请内存也就是去找堆&#xff0c;一个程序中会有很多地方要去找堆&#xff0c;这样子效率会很低下&#xff0c;为了…

vue之我不会 计算属性 vuex 路由 插槽

一、计算属性 例子&#xff1a; 注意&#xff1a;调用计算属性时&#xff0c;不可以带括号&#xff0c;那样调用的就是方法&#xff0c;如&#xff1a;以下调用fullName时不可funnName() <div id"root">姓&#xff1a;<input type"text" v-model&…

化妆风格识别系统源码分享

化妆风格识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

[2025]基于微信小程序慢性呼吸系统疾病的健康管理(源码+文档+解答)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

新手学习python第九天——加速学习

大家周末好&#xff0c;今天是周六北京时间07&#xff1a;50达到实验室&#xff0c;刚刚复习完昨天的内容&#xff0c;今天感冒有所好转&#xff0c;下午课题组有聚餐还是开心的&#xff0c;但今天的学习内容也不要落下。 ————08&#xff1a;24————开始学习———— 1…

SpringCloud微服务实现服务降级的最佳实践

Spring Cloud是一种用于快速构建分布式系统的框架&#xff0c;它提供了许多有用的功能&#xff0c;其中包括服务降级。 服务降级是一种保护机制&#xff0c;它可以在面临高并发或故障时保持服务的稳定性。当系统资源不足或服务出现故障时&#xff0c;服务降级可以通过关闭一些功…

为什么AI在广告投放上受追捧,创意上却饱受非议

AI代表着人类科技的未来&#xff0c;这已经是营销圈的共识&#xff0c;从网络上各个机构的解读来看&#xff0c;AI的奇点似乎正在临近。 AI人工智能对人类社会的震撼有两次标志性的事件&#xff1a;一次是AlphaGo战胜李世石&#xff0c; 我相信大多数人了解人工智能的开始&…

为什么是华为最先做出三折叠?这些黑科技硬核门槛缺一不可

一款起售价19999的手机&#xff0c;预约人数竟达到了600万&#xff0c;全球首款三折叠手机Mate XT到底有什么魔力&#xff0c;可以做到还未上市就引爆市场&#xff1f;看完这篇文章&#xff0c;你就知道何谓“科技新物种”。 9月7日12:08&#xff0c;华为Mate XT非凡大师开启预…

技术贴:电脑端企业微信双开教程!

软件双开的实现&#xff0c;很多小伙伴用的都是修改注册表的方式&#xff0c;这里我再介绍一个办法&#xff1a; 电脑桌面先新建一个 txt 文档&#xff0c;将下方命令全部复制&#xff0c;粘贴在 txt 文件中。 reg add HKEY_CURRENT_USER\Software\Tencent\WXWork /v multi_i…

C++第十二节课 模板初阶和string引入

一、函数模板 我们不需要写具体的函数&#xff0c;而是写这个函数的模板&#xff0c;编译器会根据模板生成对应的函数&#xff1b; template<typename T> template<class T> 两者的作用是等效的&#xff01; 用模板完成的功能有时候也叫泛型编程&#xff1b; …

【分立元件】案例:新人加了个TVS管为什么可能导致系统不能正常工作

因为最近在带多个新人,让其设计原理图和PCB总会发现各种电路问题点。比如TVS管接法问题。 TVS是一种限压型的过压保护器,它将过高的电压钳制至一个安全范围,藉以保护后面的电路,有着比其它保护元件更快的反应时间,这使TVS可用在防护lighting、switching、ESD等快速破坏性瞬…

JAVA虚拟机----JVM

(一)认识JVM JVM 是 Java Virtual Machine 的简称&#xff0c;意为 Java虚拟机。 虚拟机是指通过软件模拟的具有完整硬件功能的、运⾏在⼀个完全隔离的环境中的完整计算机系统。 常⻅的虚拟机&#xff1a;JVM、VMwave、Virtual Box。 &#xff08;二&#xff09;JVM运…

Linux进阶命令-重定向

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 经过上一章Linux日志的讲解&#xff0c;我们对Linux系统自带的日志服务已经有了一些了解。我们接下来将讲解一些进阶命令&am…

5、PointNeXt

5、PointNeXt PointNeXt论文&#xff1a;PointNeXt 关于PointNeXt实际上仅仅是在PointNet的基础上做了一些改进&#xff0c;从它的全称就可以看出&#xff0c;Revisiting PointNet with Improved Training and Scaling Strategies&#xff0c;在PointNet的基础上&#xff0c;引…

前端常用的主流框架有哪些

前端开发中&#xff0c;有几个主流框架非常受欢迎&#xff0c;它们为开发者提供了丰富的功能和高效的开发体验。以下是一些当前最常用的前端主流框架&#xff1a; React&#xff1a; React 是由 Facebook 开发的一个用于构建用户界面的 JavaScript 库。它鼓励使用组件化的开发模…