c语言通过前序遍历构建二叉树

前言:

        在链式二叉树中,我们一般都是通过一个建立好的二叉树从而算出他的前序遍历,那么如何通过一个前序遍历来创建一个二叉树呢,本文将详细解读前序遍历每一个步骤是如何创建二叉树的。

1、分析前序遍历,构建出二叉树

        现有前序遍历:abc##de#g##f###,其中‘#’号表示NULL。由于前序遍历的顺序为:根结点-左子树-右子树,因此该前序遍历的第一个字符表示的是整个树的根结点,既字符a是根结点。下一个字符按照前序遍历的顺序应该是根结点a的左子树也就是b为a的左子树:


        这时候根(a)-左子树(b)的顺序已经走完,下一个理应是右子树,但是这时候b被看成了一个根结点,因此顺序又回到了根结点(b)-左子树-右子树,该前序遍历的下一个字符c被当成b的左子树:


        此时根(b)-左子树(c)顺序已经走完,按理来说应该到根(b)-左子树(c)-右子树(#),但是情况和上面一样,把c看成了根结点,则该前序序列的下一个字符‘#’号是c的左子树。但是‘#’号表示的是NULL,因此走到‘#'号时不会把’#‘当成一个根结点来看待了,所以会走完根结点c的顺序,既:根(c)-左子树(#)-右子树(#),如下图:


        现在终于完整的走完了一次前序遍历的顺序,接着就是往上”收回“,因为结点c下面两个孩子结点都为NULL,因此不会再往下走。结点c遍历结束后,表示的是b的左子树完成, 那么接着刚刚结点b的前序遍历:此时根(b)-左子树(c),接下来应该是根结点b的右子树,因此把该序列的下一个字符d看成是b的右子树:


        现在虽然根结点b完成了他的树,但是结点d的两个孩子结点也是需要填写的,逻辑也是前序遍历的逻辑:根结点(d)-左子树-右子树,该序列的下一个字符看成d的左子树:


        此时,结点e不是NULL,他还是一个结点,因此他也会被当成根结点,继续属于他的顺序,则下一个字符’#‘号是e的左子树,’#‘号不会被当成根结点,因此g被看成是e的右子树:


        结点g的逻辑同结点e一样,他也被看成一个结点,因此也有属于g的孩子结点,则该序列的后两个’#‘号是g的左右孩子:


        此时g结点的孩子结点都是NULL,代码g作为根结点的前序顺序结束,这时候要进行”收回“,通过上图可以看到,d的右子树还没有进行遍历,因此”收回“至d的左子树处,遍历d的右子树:


        可以看到,d的右子树并不是NULL,是一个结点f,因此逻辑相同: 


        f的两个孩子结点都是NULL,表示f作为根结点的前序顺序完成,又表示根结点a的左子树全部完成,因此“收回”至根结点a的左子树处,接下来就是遍历根节点a的右子树,可以看到序列中只剩下一个’#‘号了,所以根结点a的右子树其实就是一个NULL:


2、代码实现及验证

        将以上思想转换成代码形式,并且用中序遍历打印该树,观察结果的正确性,前序遍历:abc##de#g##f###可以看成是字符串,因此用数组的形式将其存储。构建二叉树代码如下:

#include <stdio.h>
#include <stdlib.h>//创建树节点结构体
typedef int TreeDataType;//int类型重定义
typedef struct TreeNode
{TreeDataType data;struct TreeNode* left;struct TreeNode* right;
}TNode;//创建树节点
TNode* CreatTreeNode(int x)
{TNode* treenode = (TNode*)malloc(sizeof(TNode));if (treenode == NULL){perror("malloc");return NULL;}treenode->data = x;treenode->left = NULL;treenode->right = NULL;return treenode;//返回创建号的树节点地址
}//利用前序遍历构建树
TNode* CreatTree(char* arr, int* pi)
{if (arr[(*pi)] == '#')//当数组内遍历到#号,表示空,直接返回即可{(*pi)++;//遍历数组return NULL;}TNode* poi = CreatTreeNode(arr[(*pi)]);//走到此处表示遍历数组遇到的不是#,则创建结点(*pi)++;//遍历数组//递归思想poi->left = CreatTree(arr, pi);//将该结点看成根结点,去创建他的左子树poi->right = CreatTree(arr, pi);//将该结点看成根结点,去创建他的右子树return poi;//返回该结点的地址给到上一层结点,将他们相连(递归思想)
}//中序遍历
void InOrder(TNode* boot)
{if (boot == NULL){return;}InOrder(boot->left);//遍历左子树printf("%c ", boot->data);//打印该结点的值,既打印根节点的值InOrder(boot->right);//遍历右子树
}//主函数
int main() {char arr[100];//创建一个数组用于保存前序遍历,因此可以把前序遍历看成一个字符串scanf("%s", arr);int i = 0;//i为数组的下标,用于遍历数组TNode* root = CreatTree(arr, &i);//用前序遍历构建树InOrder(root);//中序遍历检查结果return 0;
}

        运行结果:

        从运行结果来看,中序遍历为:c b e g d f a,中序遍历的顺序为:左子树-根-右子树,根据上文构建出来的二叉树来进行验证该树的中序遍历是否为c b e g d f a:


        待到方框1中遍历结束后,表示b的左子树遍历完成,根据中序遍历的顺序,下一步就是遍历根结点也就是结点b,然后是结点b的右子树d:


        当遍历结点d时,会把结点d当作根结点,因此顺序重新由左子树-根-右子树开始,会先遍历e结点,当遍历到e的右子树g时,逻辑也一样会把g当作根结点从而重新开始遍历g结点:


        方框4中结束后,表示d的左子树遍历完成,根据中序遍历顺序,下一个是遍历根也就是根结点d,然后是遍历d的右子树f,遍历结点f道理也一样,把f当成根结点并从f的左子树开始遍历:


        方框6中结束后,表示根结点a的左子树全部遍历完毕,按照中序遍历顺序,左子树完成后下一个是根结点,因此遍历根结点a,最后是遍历a的右子树,a的右子树为空因此最终的顺序如下:

        从上图可以看到最后的顺序和代码执行后的中序遍历顺序是一样的,因此说明该代码确实可以通过前序遍历创建出二叉树。 

结语:

        以上就是关于如何通过前序遍历创建出一个二叉树,尽管这个二叉树很复杂,但是最终还是能够实现出来的,并且其中的每一步遍历都蕴含了递归的思想,细品则会发现更深层次的递归二叉树实现。希望通过以上步骤能够加深你对二叉树的理解(●'◡'●)。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法】链表-20231124

这里写目录标题 一、83. 删除排序链表中的重复元素二、206. 反转链表三、234. 回文链表 一、83. 删除排序链表中的重复元素 简单 1.1K 相关企业 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例…

Spring Boot + hutool 创建海报图片

Spring Boot hutool 创建海报图片 /*** 分享,生成图片* param id* return*/GetMapping("/getShareImg")public void getShareImg(String id,HttpServletResponse response) throws IOException {CouponConsignSaleClassify byId couponConsignSaleClassifyService…

机器学习之自监督学习(四)MoCo系列翻译与总结(一)

Momentum Contrast for Unsupervised Visual Representation Learning Abstract 我们提出了“动量对比”&#xff08;Momentum Contrast&#xff0c;MoCo&#xff09;来进行无监督的视觉表示学习。从对比学习的角度来看&#xff0c;我们将其视为字典查找&#xff0c;通过构建…

C#/.NET/.NET Core推荐学习书籍(已分类)

前言 古人云&#xff1a;“书中自有黄金屋&#xff0c;书中自有颜如玉”&#xff0c;说明了书籍的重要性。作为程序员&#xff0c;我们需要不断学习以提升自己的核心竞争力。以下是一些优秀的C#/.NET/.NET Core相关学习书籍&#xff0c;值得.NET开发者们学习和专研。书籍已分类…

医学图像分割:U_Net 论文阅读

“U-Net: Convolutional Networks for Biomedical Image Segmentation” 是一篇由Olaf Ronneberger, Philipp Fischer, 和 Thomas Brox发表的论文&#xff0c;于2015年在MICCAI的医学图像计算和计算机辅助干预会议上提出。这篇论文介绍了一种新型的卷积神经网络架构——U-Net&a…

Linux【安全 01】云服务器主机安全加固(修改SSHD端口、禁用登陆失败的IP地址、使用密钥登录)

云服务器主机安全加固 1.SSH登录尝试的系统日志信息2.安全加固方法2.1 修改SSHD端口2.2 禁用登陆失败的IP地址2.3 使用密钥登录 3.总结 1.SSH登录尝试的系统日志信息 Last failed login: Sat Oct 7 14:10:39 CST 2023 from xxx.xx.xx.xxx on ssh:notty There were 10 failed …

构建未来:云计算 生成式 AI 诞生科技新局面

目录 引言生成式 AI&#xff1a;开发者新伙伴云计算与生成式 AI 的无缝融合亚马逊云与生成式 AI 结合的展望/总结我用亚马逊云科技生成式 AI 产品打造了什么&#xff0c;解决了什么问题未来科技发展趋势&#xff1a;开发者的机遇与挑战结合实践看未来结语开源项目 引言 2023年…

Oracle中文显示???????解决办法

项目场景&#xff1a; Oracleoracle中文显示???解决办法 问题描述 原因分析&#xff1a; Oracle中文显示???通常是由于字符集不匹配或者编码问题导致的。当数据库中的数据使用的是某种字符集&#xff0c;而客户端或者应用程序使用的是另一种字符集时&#xff0c;就会出…

JOSEF约瑟 热过载保护继电器 JR36-160,整定值100-160A

系列型号 JR36-20 1.0-1.6A热继电器 JR36-20 0.25-0.35A热继电器 JR36-20 0.32-0.5A热继电器 JR36-20 0.45-0.72A热继电器 JR36-20 0.68-1.1A热继电器 JR36-20 1.5-2.4A热继电器 JR36-20 2.2-3.5A热继电器 JR36-20 3.2-5A热继电器 JR36-20 4.5-7.2A热继电器 JR36-20 …

ES之x-pack-core-7.14.2许可证修改为白金版

X-Pack是什么 X-pack是elasticsearch的一个扩展包&#xff0c;将安全&#xff0c;警告&#xff0c;监视&#xff0c;图形和报告功能捆绑在一个易于安装的软件包中&#xff0c;虽然x-pack被设计为一个无缝的工作&#xff0c;但是你可以轻松的启用或者关闭一些功能。 主要分一下步…

什么是强化学习

1 概况 1.1 定义 强化学习&#xff08;Reinforcement Learning, RL&#xff09;是机器学习的一个重要分支&#xff0c;与监督学习和无监督学习并列。它主要涉及智能体&#xff08;agent&#xff09;在环境中通过学习如何做出决策。与监督学习的主动指导和无监督学习的数据探索…

授时小课堂——北斗卫星信号和GPS卫星信号谁更强?

北斗卫星信号好还是GPS信号更胜一筹呢&#xff1f;下面小编带大家一起来比较一下看看吧。 1. 系统覆盖范围 北斗卫星导航系统是中国自主研发的授时定位系统&#xff0c;其覆盖范围包括全球各个地区。但在海外地区&#xff0c;主要还是东南亚、南亚、中亚等地区&#xff0c;北斗…