二叉树的创建与存储,以及遍历_二叉树的二叉链表的创建-程序员宅基地

技术标签: C语言学习历程  算法  c语言  数据结构  程序人生  

树的定义

  • 树是n个节点的集合,在任何一棵非空树中有且仅有一个被称为根的结点,当n>1时,其余结点可以被分为m个互不相交的子集,其中每个子集又是一棵树,称其为根的子树

树的基本术语 

  • 结点:一个数据元素以及若干指向其子树的分支
  • 结点的度:结点所拥有的子树的棵树
  • 树的度:树中各个结点度的最大值
  • 叶子:度为0的结点称为叶子结点,又称为终端结点
  • 分支结点:度不为0的结点,又称为非终端结点
  • 结点的孩子:结点的子树的根称为该结点的孩子,该结点称为孩子结点的双亲
  • 根:没有双亲的结点
  • 结点的层次:从根开始定义,根为第一层,根的孩子所在的层为第二层,根的孩子的孩子所在的层为第三层,以此类推
  • 树的深度:树的叶子结点所在的最大层次
  • 兄弟结点:双亲一样的结点
  • 堂兄弟:双亲位于同一层的结点
  • 结点祖先:从根到该结点所经过的分支上的所有结点均称为该结点的祖先
  • 子孙:某个结点的子树上的所有结点都称为该节点的子孙
  • 有序树:若将树中各结点的各子树看成从左至右是有顺序的不能任意交换位置,则称该树为有序树,否则为无序树
  • 森林:m(m>=0)棵互不相交的树的集合,所以任意一棵树都可以看成是由根和其子树森林所构成的

二叉树的定义

  • 二叉树是n个节点的集合 ,在任何一棵非空二叉树中有且仅有一个被叫做根的节点,若n>1,则其余节点被分成两个互不相交的子集,其中每一个子集又是一棵二叉树,分别称为左子树和右子树,所以二叉树的定义是递归的可以用递归算法来创建一棵二叉树。
  • 满二叉树:除了叶子结点以外其余结点的度全为2的二叉树
  • 完全二叉树:在满二叉树的最后一层上从右至左连续去除若干个叶子结点便得到了一棵完全二叉树。

二叉树的特点

  1. 二叉树中各结点的度小于等于2
  2. 二叉树是有序树
  3. 二叉树的每一层至多有pow(2,n-1)个结点
  4. 深度为k的二叉树至多有pow(2,k)-1个结点
  5.  若二叉树中叶子结点即度为0的结点的个数为n0,度为2的结点的个数为n2,则n0=n2+1
  6. 对于一棵有n个节点的完全二叉树其深度为([log2(n)]+1)
  7. 将完全二叉树从左至右从上至下按层次编号1,2.....则对任意节点i,满足若i=1,则该结点为根节点无双亲,若i>1则i的双亲为[i/2];若2i>n则结点i无左孩子反之结点i的左孩子为2i,若(2i+1 )大于n,则结点i无右孩子反之结点i的右孩子为(2i+1)

二叉树的存储结构

  • 二叉树的顺序存储表示 

对于完全二叉树而言,我们用一组地址连续的存储单元即一维数组依次从上至下从左至右的存储该完全二叉树 中的节点元素,对于普通的二叉树我们将其结点与其对应的完全二叉树相对照,存储在一维数组中,例如:

二叉树的顺序存储的特点

  1. 结点间的关系蕴含在其存储位置中
  2. 浪费空间,适用于存储满二叉树或者完全二叉树 

说明:鉴于顺序存储的二叉树比较简单,读者可以自己尝试定义二叉树的结点,然后将结点存储在一维数组中即可。

 

  •  二叉树的链式存储表示

二叉树的结点可以用结构体类型来表示,定义不同的结点结构,可以构成不同的链式存储结构。

二叉链表:存储该二叉树的链表的结点的指针域有两个,分别指向该节点的左右孩子结点

三叉链表: 存储该二叉树的链表的结点的指针域有三个,分别指向左孩子结点,双亲结点,以及右孩子结点

  • 二叉树的三种遍历方法
  1. 先序遍历:先访问根节点,再访问左子树和右子树,对左子树和右子树的访问也遵循根左右的原则
  2. 中序遍历:先访问左子树,再访问根节点,最后访问右子树,对左子树和右子树的访问也遵循左根右的原则
  3. 后序遍历:先访问左子树,再访问右子树,最后访问根节点,对左子树和右子树的访问也遵循左右根的顺序 

由以上定义可知对二叉树的三种遍历方法都是递归的所以设计遍历方法时需要用到递归 

下面演示用二叉链表来表示如图所示的二叉树,以及采用三种遍历方法遍历该二叉树的过程,在程序代码中给出了完整的注释:

 

  1. 第一步定义二叉树的结点,用结构体类型来定义节点结构
    typedef struct BitNode
    {
    	char value;                                              //数据域,用于存放该节点的值
    	struct BitNode* lchild, * rchild;                              //二叉链表的节点中有两个指针与分别指向该节点的左右孩子节点
    	
    
    }BitNode,*Bitree;       //为定义的结构体类型起别名为BitNode,以及为该结构体类型的指针起别名为Bitree,此时执行语句"BitNode a"相当于执行语句"struct BitNode a",执行语句"Bitree a"相当于执行语句"BitNode *a"
  2.  第二步创建该二叉树

    Bitree CreateTree(Bitree head)
    {
    	char value;
    	Bitree p;
    	//p = head = NULL;           //相当于每次调用该函数都会把头指针设置为空指针;这显然是错误的
    
    	printf("请输入节点的值\n");
    	scanf("%c", &value);
    	getchar();
    
    	if (value == '#')
    		return NULL;                         //创建二叉树,当输入#号时表示不再继续输入返回空指针给上一级调用对象,即将上一级节点没有左孩子或者没有右孩子,理解这一点很重要,函数的返回值是所定义的结构体类型的指针
    	else
    	{
    
    		p = (Bitree)malloc(sizeof(BitNode));
    		p->value = value;
    		if (!head)
    			head = p;          //创建头指针;
    
    		printf("请输入%c的左子树的根节点\n", value);
    		p->lchild = CreateTree(head);           //递归创建左子树;
    		printf("请输入%c的右子树的根节点\n", value);
    		p->rchild = CreateTree(head);           //递归创建右子树;
    
    		return p;              //函数出口返回的是指向创建的二叉树的第一个结点的指针;
    
    	}
    }

  3. 先序遍历该二叉树

    void FirstOrderTraverse(Bitree p)
    {
    	if (p == NULL)
    		return;
    	printf("%c\t", p->value);
    	FirstOrderTraverse(p->lchild);
    	FirstOrderTraverse(p->rchild);
    
    
    }

  4. 中序遍历该二叉树

    void MiddleOrderTraverse(Bitree p)
    {
    	if (p == NULL)
    		return;
    	MiddleOrderTraverse(p->lchild);
    	printf("%c\t", p->value);
    	MiddleOrderTraverse(p->rchild);
    
    
    }
    

  5. 后序遍历该二叉树

    void PostOrderTraverse(Bitree p)                        //后序遍历二叉树即最后访问根节点
    {
    	if (p == NULL)
    		return;
    
    	PostOrderTraverse(p->lchild);                         //此递归函数都没有递归结束条件,怎么从递归中出来呢?递归结束条件怎么能不写
    	PostOrderTraverse(p->rchild);
    	printf("%c\t", p->value);
    }
    
    
    

程序完整代码以及运行结果截图:

代码:

//二叉树的定义与储存,用二叉链表存储二叉树


#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>            //该头文件包含了malloc函数的声明;

//定义二叉树的节点结构,采用二叉链表存储该二叉树
typedef struct BitNode
{
	char value;                                              //数据域,用于存放该节点的值
	struct BitNode* lchild, * rchild;                              //二叉链表的节点中有两个指针与分别指向该节点的左右孩子节点


}BitNode, * Bitree;//为定义的结构体类型起别名为BitNode,
//以及为该结构体类型的指针起别名为Bitree,此时执行语句"BitNode a"相当于执行语句"struct BitNode a",执行语句"Bitree a"相当于执行语句"BitNode *a"


//创建一棵不带头节点的二叉树的函数
Bitree CreateTree(Bitree head);
//后序遍历一棵树 
void FirstOrderTraverse(Bitree p);
void MiddleOrderTraverse(Bitree p);
void PostOrderTraverse(Bitree p);



int main()
{
	Bitree head = NULL,p;
	head = CreateTree(head);
	if (head)
		printf("树创建成功\n");
	else
		printf("树创建失败\n\n\n");

	printf("先序遍历该树的结果\n");
	FirstOrderTraverse(head);
	printf("\n");

	printf("中序遍历该树的结果\n");
	MiddleOrderTraverse(head);
	printf("\n");

	printf("后序遍历该树的结果\n");
	PostOrderTraverse(head);
	printf("\n");


	return 0;

}

//函数实现,按照中序遍历的思想创建该二叉树,即先创建根节点再创建根节点的左子树和右子树,根据此思想创建二叉树可以用到递归算法
Bitree CreateTree(Bitree head)
{
	char value;
	Bitree p;
	//p = head = NULL;           //相当于每次调用该函数都会把头指针设置为空指针;这显然是错误的

	printf("请输入节点的值\n");
	scanf("%c", &value);
	getchar();

	if (value == '#')
		return NULL;                         //创建二叉树,当输入#号时表示不再继续输入返回空指针给上一级调用对象,即将上一级节点没有左孩子或者没有右孩子,理解这一点很重要,函数的返回值是所定义的结构体类型的指针
	else
	{

		p = (Bitree)malloc(sizeof(BitNode));
		p->value = value;
		if (!head)
			head = p;          //创建头指针;

		printf("请输入%c的左子树的根节点\n", value);
		p->lchild = CreateTree(head);           //递归创建左子树;
		printf("请输入%c的右子树的根节点\n", value);
		p->rchild = CreateTree(head);           //递归创建右子树;

		return p;              //函数出口返回的是指向创建的二叉树的第一个结点的指针;

	}
}


void FirstOrderTraverse(Bitree p)
{
	if (p == NULL)
		return;
	printf("%c\t", p->value);
	FirstOrderTraverse(p->lchild);
	FirstOrderTraverse(p->rchild);


}


void MiddleOrderTraverse(Bitree p)
{
	if (p == NULL)
		return;
	MiddleOrderTraverse(p->lchild);
	printf("%c\t", p->value);
	MiddleOrderTraverse(p->rchild);


}




void PostOrderTraverse(Bitree p)                        //后序遍历二叉树即最后访问根节点
{
	if (p == NULL)
		return;

	PostOrderTraverse(p->lchild);                         //此递归函数都没有递归结束条件,怎么从递归中出来呢?递归结束条件怎么能不写
	PostOrderTraverse(p->rchild);
	printf("%c\t", p->value);
}


运行结果截图

 

 

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mili_j/article/details/129570089

智能推荐

leetcode 172. 阶乘后的零-程序员宅基地

文章浏览阅读63次。题目给定一个整数 n,返回 n! 结果尾数中零的数量。解题思路每个0都是由2 * 5得来的,相当于要求n!分解成质因子后2 * 5的数目,由于n中2的数目肯定是要大于5的数目,所以我们只需要求出n!中5的数目。C++代码class Solution {public: int trailingZeroes(int n) { ...

Day15-【Java SE进阶】IO流(一):File、IO流概述、File文件对象的创建、字节输入输出流FileInputStream FileoutputStream、释放资源。_outputstream释放-程序员宅基地

文章浏览阅读992次,点赞27次,收藏15次。UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。文件字节输入流:每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1。注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。UTF-8字符集:汉字占3个字节,英文、数字占1个字节。GBK字符集:汉字占2个字节,英文、数字占1个字节。GBK规定:汉字的第一个字节的第一位必须是1。_outputstream释放

jeecgboot重新登录_jeecg 登录自动退出-程序员宅基地

文章浏览阅读1.8k次,点赞3次,收藏3次。解决jeecgboot每次登录进去都会弹出请重新登录问题,在utils文件下找到request.js文件注释这段代码即可_jeecg 登录自动退出

数据中心供配电系统负荷计算实例分析-程序员宅基地

文章浏览阅读3.4k次。我国目前普遍采用需要系数法和二项式系数法确定用电设备的负荷,其中需要系数法是国际上普遍采用的确定计算负荷的方法,最为简便;而二项式系数法在确定设备台数较少且各台设备容量差..._数据中心用电负荷统计变压器

HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板_网页设计成品百度网盘-程序员宅基地

文章浏览阅读7k次,点赞4次,收藏46次。HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水平作业_网页设计成品百度网盘

【Jailhouse 文章】Look Mum, no VM Exits_jailhouse sr-iov-程序员宅基地

文章浏览阅读392次。jailhouse 文章翻译,Look Mum, no VM Exits!_jailhouse sr-iov

随便推点

chatgpt赋能python:Python怎么删除文件中的某一行_python 删除文件特定几行-程序员宅基地

文章浏览阅读751次。本文由chatgpt生成,文章没有在chatgpt生成的基础上进行任何的修改。以上只是chatgpt能力的冰山一角。作为通用的Aigc大模型,只是展现它原本的实力。对于颠覆工作方式的ChatGPT,应该选择拥抱而不是抗拒,未来属于“会用”AI的人。AI职场汇报智能办公文案写作效率提升教程 专注于AI+职场+办公方向。下图是课程的整体大纲下图是AI职场汇报智能办公文案写作效率提升教程中用到的ai工具。_python 删除文件特定几行

Java过滤特殊字符的正则表达式_java正则表达式过滤特殊字符-程序员宅基地

文章浏览阅读2.1k次。【代码】Java过滤特殊字符的正则表达式。_java正则表达式过滤特殊字符

CSS中设置背景的7个属性及简写background注意点_background设置背景图片-程序员宅基地

文章浏览阅读5.7k次,点赞4次,收藏17次。css中背景的设置至关重要,也是一个难点,因为属性众多,对应的属性值也比较多,这里详细的列举了背景相关的7个属性及对应的属性值,并附上演示代码,后期要用的话,可以随时查看,那我们坐稳开车了······1: background-color 设置背景颜色2:background-image来设置背景图片- 语法:background-image:url(相对路径);-可以同时为一个元素指定背景颜色和背景图片,这样背景颜色将会作为背景图片的底色,一般情况下设置背景..._background设置背景图片

Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏8次。Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程

PyCharm2021安装教程-程序员宅基地

文章浏览阅读10w+次,点赞653次,收藏3k次。Windows安装pycharm教程新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入下载安装PyCharm1、进入官网PyCharm的下载地址:http://www.jetbrains.com/pycharm/downl_pycharm2021

《跨境电商——速卖通搜索排名规则解析与SEO技术》一一1.1 初识速卖通的搜索引擎...-程序员宅基地

文章浏览阅读835次。本节书摘来自异步社区出版社《跨境电商——速卖通搜索排名规则解析与SEO技术》一书中的第1章,第1.1节,作者: 冯晓宁,更多章节内容可以访问云栖社区“异步社区”公众号查看。1.1 初识速卖通的搜索引擎1.1.1 初识速卖通搜索作为速卖通卖家都应该知道,速卖通经常被视为“国际版的淘宝”。那么请想一下,普通消费者在淘宝网上购买商品的时候,他的行为应该..._跨境电商 速卖通搜索排名规则解析与seo技术 pdf

推荐文章

热门文章

相关标签