二叉树的创建和遍历实现

2023-11-18

1 前言

提到**树(Tree)**结构,很容易联想到”大树“,想到这是“一对多关系“特性的数据结构,其相关的名词、概念很多:

  • 子树(SubTree)、结点(Node)、根结点(Root)、叶子(Leaf)/ 终端结点、分支结点 / 非终端结点、内部结点、孩子(Child)、双亲(Parent)、兄弟(Sibling)、堂兄弟、祖先、子孙
  • 层次(Level)、度(Degree)、深度(Depth)/ 高度
  • 空树、有序树、只有根结点的树、普通树、二叉树、斜树、满二叉树、完全二叉树等等

这里对于这些个概念不再一一展开介绍,对以上名词陌生的小伙伴可以自行学习。可参考:二叉树的相关概念

2 二叉树

首先得是颗树,然后是他的节点是两个,限制了分叉数量2。

2.1 顺序储存(数组实现)

图示为一颗满二叉树,我们按照 层次(Level)从0-n(数组下标)进行编号,这样就可以储存二叉树了。
犯得上发射点

char* arr;
arr[0] = 'A';
arr[1] = 'B';
arr[2] = 'C';
arr[3] = 'D';
arr[4] = 'E';
arr[5] = 'F';
arr[6] = 'G';

这样我们便可以使用数组存储二叉树了。
如果对于一个普通二叉树呢?可以构造出一个满/完全二叉树,对于虚构得节点用’#‘代替。如下一颗普通二叉树,用#补成满二叉树。
在这里插入图片描述

char* arr;
arr[0] = 'A';
arr[1] = 'B';
arr[2] = 'C';
arr[3] = '#';
arr[4] = '#';
arr[5] = '#';
arr[6] = 'G';

这里的问题是,将浪费内存,极端点,如果变成了右斜树,顺序储存将浪费大量空间。

2.2 链式储存(链表实现)

链表对我们来说并不陌生,通过结点加箭头的方式来存储数据,节点表示数据元素箭头表示节点间的关系
那二叉树的链式结构应该什么样呢?在开始就说二叉树是只有两个分叉的树,那么很简单,二叉树由一个的节点和两个分支组成,即:

  • 数据元素
  • 左子树分支(结点的左孩子)
  • 右子树分支(结点的右孩子)
    那现在这个结构就很显然了
/*二叉树的结点的结构体*/
typedef struct BiTNode {
    char data; //数据域
    struct BiTNode *lchild; //左孩子指针
    struct BiTNode *rchild; //右孩子指针
} BiTNode, *BiTree;;

实际上二叉链表是比较常用的存储方式,当然也可以在结构中加入struct BiTNode *parent;可以很轻松地找到各节点的父节点,这时的链表可以称为三叉链表。

2.3二叉树的创建

二叉树百科中递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。
既然二叉树是通过递归定义的,所以想要创建一颗二叉树,这里可以借助递归去创造。
前面在构建顺序链表时,将普通二叉树补全成满/完全二叉树。这里当拿到一颗普通二叉树时,能迅速将其个节点内容补齐,如下:
在这里插入图片描述
这里使用“#”代替“NULL”,补全子节点、叶节点的结构,而NULL(#)节点是不指向任何节点的。
我们通过先序遍历的方法输出下补充后的二叉树,即为:ABD##EG###C#F##,同样也可以中序遍历,或者后序遍历像这样输出节点,只需对创建函数对应修改即可。
按照前序遍历的顺序创建二叉树,代码如下:

void CreateBiTree(BiTree *T)
{
    char ch;
    scanf("%c",&ch);
    if (ch == '#')
        *T = NULL;  //保证是叶结点
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        if (!*T) return;
        (*T)->data = ch;//生成结点
        CreateBiTree(&(*T)->lchild);//构造左子树
        CreateBiTree(&(*T)->rchild);//构造右子树    
    }
}

值得注意的是,CreateBiTree(BiTree *T),的入参是二级指针。一级指针不行嘛,遍历时用的都是一级指针,学生时代并没太注意,只是记下了,稍后代码说明。

这里使用的递归创建和前序遍历是一致的。

3 二叉树遍历

3.1递归实现

前面进行二叉树创建就是根据二叉树的定义,通过递归实现了二叉树的创建,当然遍历也是如此,无非就三件事:访问根结点、找左子树、找右子树。

  • 先序遍历
/*
*先序遍历
*root:指向根根节点的指针
*/
void preOrderTranversal(BiTNode *root){
    if(root == NULL) return;//节点(根或者子根节点)
    printf("%c\t",root->data);
    preOrderTranversal(root->lchild);//遍历左子树
    preOrderTranversal(root->rchild);//遍历右子树
}
  • 中序遍历

/*中序遍历*/
void inorder_traversal(BiTNode *root)
{
    if (root == NULL) { //若二叉树为空,做空操作
        return;
    }
    inorder_traversal(root->lchild); //递归遍历左子树
    printf("%c\t", root->data); //访问根结点
    inorder_traversal(root->rchild); //递归遍历右子树
}
  • 后续遍历
/*后序遍历*/
void postorder_traversal(BiTNode *root)
{
    if (root == NULL) { //若二叉树为空,做空操作
        return;
    }
    postorder_traversal(root->lchild); //递归遍历左子树
    postorder_traversal(root->rchild); //递归遍历右子树
    printf("%c\t", root->data); //访问根结点
}

main函数

void main(void){
    BiTree T = NULL;
    CreateBiTree(&T);
    printf("先序遍历:");
    preOrderTranversal(T);
    printf("\n");

    printf("中序遍历:");
    inorder_traversal(T);
    printf("\n");

    printf("后序遍历:");
    postorder_traversal(T);
    printf("\n");
}

执行下看看:
在这里插入图片描述

4 栈实现二叉树遍历

代码不再这里粘贴了,另外不仅栈,队列也可以实现二叉树的遍历。
想学习的小伙伴可参考,栈实现二叉树的遍历

*为什么创建二叉树时使用二级指针做入参

请看下面代码:

#include <stdio.h>

//目标:在函数func中将b赋值给*q;
int a= 10;
int b = 100;
int *q;

void func(int *p){
    printf("fun:&p = %p p = %p\n",&p,p);
    p = &b;
    printf("fun:&p = %p p = %p\n",&p,p);
}

int main(){
    printf("&a = %p &b = %p &q = %p\n",&a,&b,&q);
    q = &a;
    printf("*q = %d q = %p &q = %p\n",*q,q,&q);
    func(q);
    printf("*q = %d q = %p &q = %p\n",*q,q,&q);
    return 0;
}

乍一看去,好像没有问题,我们将指针q传入函数func,那好先运行一下看看结果如下:
在这里插入图片描述
我们并没有成功将q指向b,这里为什么呢?
因为func的入参是q,即进入的参数是&a,然后在func起来后,在堆栈申请内存,将b的值放入该地址,但返回时也将销毁。
我们可以通过二级指针才操作,具体如下:
func()入参改为二级指针,入参为&q的地址

void func(int **p){
    printf("fun:&p = %p p = %p\n",&p,p);
    *p = &b;//改变q指向的内容
    printf("fun:&p = %p p = %p\n",&p,p);
}

int main(){
    printf("&a = %p &b = %p &q = %p\n",&a,&b,&q);
    q = &a;
    printf("*q = %d q = %p &q = %p\n",*q,q,&q);
    func(&q);
    printf("*q = %d q = %p &q = %p\n",*q,q,&q);
    return 0;
}

在这里插入图片描述
此时,将可以修改q指向的内容。我写的有点乱,可以拿笔画一画简单体会下。哈哈哈~

参考

https://www.jianshu.com/p/12848eef3452
https://cloud.tencent.com/developer/article/1819521
https://blog.csdn.net/lingling_nice/article/details/80960439

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二叉树的创建和遍历实现 的相关文章

  • 通过 SocketCAN 进行 boost::asio

    我正在考虑利用升压阿西奥 http www boost org doc libs 1 49 0 doc html boost asio html从a读取数据套接字CAN http en wikipedia org wiki SocketCA
  • 如何在 DataColumn.Expression 中使用 IF/ELSE 或 CASE?

    我有一个包含 1 列的表 状态 我想添加另一列名为 Action 的列 其值如下 如果 Status Yes 则 Action Go 否则 Action Stop 我使用以下代码添加到 操作 列中 但它不起作用 myDataTable Co
  • 在 C/C++ 中获得正模数的最快方法

    通常在我的内部循环中 我需要以 环绕 方式索引数组 因此 例如 如果数组大小为 100 并且我的代码要求元素 2 则应该给它元素 98 高级语言 例如 Python 可以简单地使用my array index array size 但由于某
  • 在 C# 中按元素相乘数组具有意想不到的性能

    我想找到按元素相乘两个数组的最佳方法 这是更广泛项目的一部分 其中性能而不是唯一的考虑因素 我今天开始用 C Linqpad 编写一些函数 因此它还没有以任何方式进行优化 下面代码的输出如下 Environment ProcessorCou
  • 如何保证对象只有一个线程

    我有以下代码 class Service public void start creates thread which creates window and goes to message loop void stop sends WM C
  • 何时使用 =default 使析构函数默认?

    尽管对构造函数使用 default 对我来说很清楚 即强制编译器在其他构造函数存在时创建默认构造函数 但我仍然无法理解这两种类型的析构函数之间的区别 那些使用 default 的 那些没有显式定义并由编译器自动生成的 我唯一想到的是 gro
  • 使用 Enumerable.OfType() 或 LINQ 查找特定类型的所有子控件

    Existed MyControl1 Controls OfType
  • 在 Xamarin 中隐藏软键盘

    如何隐藏软键盘以便在聚焦时显示Entry在 Xamarin forms 便携式表单项目中 我假设我们必须为此编写特定于平台的渲染器 但以下内容不起作用 我创建自己的条目子类 public class MyExtendedEntry Entr
  • 找不到 assimp-vc140-mt.dll ASSIMP

    我已经从以下位置下载了 Assimp 项目http assimp sourceforge net main downloads html http assimp sourceforge net main downloads html Ass
  • ASP.Net Core 内容配置附件/内联

    我正在从 WebAPI 控制器返回一个文件 Content Disposition 标头值自动设置为 附件 例如 处置 附件 文件名 30956 pdf 文件名 UTF 8 30956 pdf 当它设置为附件时 浏览器将要求保存文件而不是打
  • 单例模式和 std::unique_ptr

    std unique ptr唯一地控制它指向的对象 因此不使用引用计数 单例确保利用引用计数只能创建一个对象 那么会std unique ptr与单例执行相同 单例确保只有一个实例属于一种类型 A unique ptr确保只有一个智能指针到
  • 将二进制数据从 C# 上传到 PHP

    我想将文件从 Windows C 应用程序上传到运行 PHP 的 Web 服务器 我知道 WebClient UploadFile 方法 但我希望能够分块上传文件 以便我可以监控进度并能够暂停 恢复 因此 我正在读取文件的一部分并使用 We
  • AES 输出是否小于输入?

    我想加密一个字符串并将其嵌入到 URL 中 因此我想确保加密的输出不大于输入 AES 是可行的方法吗 不可能创建任何始终会创建比输入更小的输出的算法 但可以将任何输出反转回输入 如果您允许 不大于输入 那么基本上您只是在谈论同构算法alwa
  • 将标量添加到特征矩阵(向量)

    我刚刚开始使用 Eigen 库 无法理解如何向所有矩阵成员添加标量值 假设我有一个矩阵 Eigen Matrix3Xf mtx Eigen Matrix3Xf Ones 3 4 mtx mtx 1 main cxx 104 13 error
  • 是否有相当于 Clang/LLVM 的 .spec 文件,在哪里可以找到参考?

    The gcc驱动程序可以配置为使用特定的链接器 特定的选项和其他细节 例如覆盖系统头 specs files 当前 截至撰写本文时 GCC 版本 4 9 0 的手册此处描述了规范文件 https gcc gnu org onlinedoc
  • 更改 Windows Phone 系统托盘颜色

    有没有办法将 Windows Phone 上的系统托盘颜色从黑色更改为白色 我的应用程序有白色背景 所以我希望系统托盘也是白色的 您可以在页面 XAML 中执行此操作
  • C++ Streambuf 方法可以抛出异常吗?

    我正在尝试找到一种方法来获取读取或写入流的字符数 即使存在错误并且读 写结束时间较短 该方法也是可靠的 我正在做这样的事情 return stream rdbuf gt sputn buffer buffer size 但如果streamb
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959
  • ASP.NET Core MVC 视图组件搜索路径

    在此处的文档中 https learn microsoft com en us aspnet core mvc views view components view aspnetcore 2 2 https learn microsoft

随机推荐

  • 关于java.lang.UnsatisfiedLinkError的小案例

    在许多项目中我们都会用到第三方动态库 so文件 但是往往会引来很多烦恼 比如 java lang UnsatisfiedLinkError 06 17 15 52 08 097 7876 7916 com ishow scan E Andr
  • 前端js和jq中select下拉框

    获取select选中的option的值 ddlRegType find option selected val 获取select选中的text ddlRegType find option selected text 获取select选中的
  • 03-postgresql报错ERROR: operator does not exist: numeric = character varyin

    现在要把数据库换成postgresql 但在转换过程中发现postgresql对传入的参数类型匹配相当严格 如select from user where a b 假设a类型为numeric 而传入的b为string的话postgresql
  • wifi类物联产品配网前言

    文章目录 1 SmartConfig配网 仅支持2 4G 2 web方式配网 3 一键配网 BLE 传统蓝牙和wifi 3 1 BLE方式 3 2 传统蓝牙方式 3 3 wifi方式 3 4 4G 网口或其他直连设备 3 5 其他配网方式
  • Linux 以root用户登录无法启动VSCode

    Linux 以root用户登录无法启动VSCode 环境 Ubuntu18 04 VSCode 复现 以root用户登录Ubuntu后单机VSCode图标打开VSCode BUG 无法打开VSCode 原因 VSCode默认不允许以root
  • vc扩展名

    APS 存放二进制资源的中间文件 VC把当前资源文件转换成二进制格式 并存放在APS文件中 以加快资源装载速度 资源辅助文件 BMP 位图资源文件 BSC 浏览信息文件 由浏览信息维护工具 BSCMAKE 从原始浏览信息文件 SBR 中生成
  • NoSql的四大类型

    NoSQL Not Only Sql 泛指非关系型的数据库 区别于关系数据库 它们不保证关系数据的ACID特性 NoSQL是一项全新的数据库革命性运动 其拥护者们提倡运用非关系型的数据存储 相对于铺天盖地的关系型数据库运用 这一概念无疑是一
  • 【扩散模型】3、DDIM

    文章目录 一 背景 二 DDIM 如何改进 2 1 DDPM 的原理回顾 2 2 DDIM 的非马尔科夫前向扩散过程 2 3 非马尔科夫扩散逆过程的采样 2 4 加速采样 Respacing 三 效果 论文 Denoising Diffus
  • 3D CG软件blender入门教程:手把手教你使用方法

    翻译 BeforeDawn大家好 我是bpm 目前在做一些设计师与技术总监相关的工作 这篇文章主要以blender这个软件作为切入点来为大家讲解一下3D CG软件blender相关概要以及使用的方法 blender是什么那么 大家知道这个名
  • 【Matlab】LM迭代估计法

    简介 在最近的传感器校准算法学习中 有一些非线性的代价函数求解使用最小二乘法很难求解 使用LM算法求解会简单许多 因此学习了一下LM算法的基础记录一下 LM 优化迭代算法时一种非线性优化算法 可以看作是梯度下降与高斯牛顿法的结合 综合了两者
  • 301跳转:http跳转https不带www跳转到带www

    写在 htaccess中 一 http跳转https RewriteCond SERVER PORT 443 RewriteRule https SERVER NAME 1 R 301 L 二 不带www跳转到带www RewriteCon
  • shell脚本-统计字符串中数字字母的个数

    bin bash read p 请输入一个字符串 str count1 0 count2 0 count3 0 count4 0 num str num for i in seq 0 num do ch str i 1 echo n ch
  • Mac 不小心断开移动硬盘导致磁盘无法读取和加载(顺利解决!)

    目录 1 问题 2 解决 2 1 终端中执行 diskutil list 2 2 输入 sudo diskutil mount dev disk0 disk1 disk2 同理 情况一 情况二 情况三 1 问题 不小心碰到USB插口 导致无
  • iOS证书(.p12)和描述文件(.mobileprovision)申请

    我们在做uniapp开发的时候 打包ios应用需要自有证书 而自有证书包含 p12和 mobileprovision这两个跟证书有关的文件 但是uniapp官方的教程 却是需要使用苹果mac系统去申请 假如没有mac电脑 则它的教程就没有参
  • Python pass 语句

    Python pass 是空语句 是为了保持程序结构的完整性 pass 不做任何事情 一般用做占位语句 Python 语言 pass 语句语法格式如下 pass 测试实例 usr bin python coding UTF 8 输出 Pyt
  • Spring boot实现Rest风格请求及底层原理

    Rest风格的介绍 如今各大公司都是使用restful风格来定义接口 restful也是一套接口的规范 restful可以使我们的接口更加简洁 快捷高效 透明 常见的Rest风格 CRUD 请求方式 对应属性 使用方式 GET 查询 表单请
  • 使用markedjs预览md文件

  • 神经网络时间序列预测PyTorch-Forecastin!

    来源 数据STUDIO 深度学习初学者 本文约5200字 建议阅读8分钟 本文为你介绍了神经网络时间序列预测PyTorch Forecastin PyTorch Forecasting 1 使用神经网络的时间序列预测对数据科学工作者和研究人
  • 地推里的t1结算啥意思

    T1结算 通常是指在地推活动中 结算员工提成的时间点 在这种情况下 T1代表第一天或第一周期的结算时间 即在活动结束后的第一天或第一周进行结算 例如 如果地推活动是在一个星期内进行的 那么T1结算可能是指在活动结束后的第一周内结算员工提成
  • 二叉树的创建和遍历实现

    1 前言 提到 树 Tree 结构 很容易联想到 大树 想到这是 一对多关系 特性的数据结构 其相关的名词 概念很多 子树 SubTree 结点 Node 根结点 Root 叶子 Leaf 终端结点 分支结点 非终端结点 内部结点 孩子 C