树的概念与二叉树的实现

news2024/9/21 18:47:52

目录

一. 树的概念

二. 访问树的方法

1. 左孩子右兄弟法

2. 双亲表示法

3. 顺序表存孩子的指针(孩子表示法)

 三. 二叉树

1. 二叉树的定义

2. 特殊二叉树

3. 二叉树的性质

4. 存储方式

四. 二叉树的前中后序遍历

1. 前序遍历

2. 中序遍历

3. 后序遍历

4.完整代码


一. 树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限节点组成的一个具有层次关系的集合。将其称为树是因为,其看起来像一颗翻转的树(根朝上,叶朝下)。最上面的节点是一个特殊的节点,称之为根节点。根节点没有前驱结点,除根节点外,其余节点被分成M(M>0)个互不相交的集合T1、T2、T3……、Tm。其中每一个集合Ti(1<=i<=m)又是一颗结构与树类似的字数。每棵子树的根节点有且只有一个前驱,可以有0个或多个后继,因此树是递归定义

 

不能出现多个节点指向同一节点的情况(如上图)

1. 节点的度一个节点链接了多少子节点,度就为多少
2.叶结点或终端节点度为0的节点,不为0就为分支节点和非终端节点,如上图E、F、G、C、H等

3. 双亲节点或父节点

若一个节点含有子节点,称此节点为其子节点的父节点,如上图A为B\C\D的父节点
4. 孩子节点或子节点一个节点含有的子树的根节点称为该节点的子节点,B\C\D为A的子节点
5. 兄弟节点具有相同父节点的节点互相称为兄弟节点,如上图B\C\D互为兄弟节点
6. 树的度一棵树中最大节点的度称为树的度,如上图,树的度为3

7. 节点的层次

从根开始定义起,根为第一层,根的子结点为第二层,以此类推
8. 树的高度书中节点的最大层次,如上图树的高度为3
9.堂兄弟节点双亲在同一层的节点互为堂兄弟,如上图E和H互为堂兄弟节点
10. 节点的祖先从根到该节点所经分支上的所有节点,上图中A,B是E\F\G的祖先
11. 子孙以某节点为根的子树中任意一个节点都称为该节点的子孙。上图中所有节点都是A的子孙

12. 森林

由m(m>0)棵互不相交的树的集合称为森林

二. 访问树的方法

用一下方法来表示上图中的树

1. 左孩子右兄弟法

链接方式如下图所示

2. 双亲表示法

让每个节点记住其父节点的位置。存储数据元素的节点由两部分组成,1.存储数据元素 2. 存储父节点位置的字段

数组下标节点名称双亲的下标
0A-1(根节点特殊处理)
1B0
2C0
3D1
4E1
5F1
6G2
7H4
8I4
3. 顺序表存孩子的指针(孩子表示法)

由链表和顺序表混合实现

节点定义1.:每个节点包含两部分,数据域来存储数据,另一部分为孩子链表指针,指向一个链表

2.  定义一个顺序表:用来定义存储所有节点的一个数组

3. 定义孩子链表:包含 数据data和指向下一个孩子链表节点的指针next

结果图如下

 三. 二叉树

1. 二叉树的定义

二叉树每个节点最多有两个子节点,被称为左子树和右子树。所有的二叉树都分为三部分:根,左子树,右子树

2. 特殊二叉树

(1) 满二叉树一个二叉树,每层的节点数都达到最大值,如果有k层,节点树为2的k次方-1就是满二叉树,如下图所示

即为满二叉树

(2)完全二叉树:树的高度是h前h-1层都是满的,最后一层不满,在最后一层从左往右都是连续的(满二叉树是特殊的完全二叉树,缺0个节点)如下图所示

即为完全二叉树

3. 二叉树的性质

(1) 若规定根节点层数为1一颗非空二叉树最多有2的(h-1)次方个节点(h为深度)

(2)根节点层数为1深度为h的二叉树最大节点个数为2的n次方-1(满二叉树)

(3)任何一个二叉树,度为0其叶节点个数为N0,度为2的分支节点个数为N2,则有N0=N2+1

(4)若规定根节点层数为1,具有n个节点的满二叉树的深度:h=logN+1(底数为2)

4. 存储方式

分别为用链表存储和用数组存储

数组存储的话可以通过双亲节点与子节点下标间的映射关系来存储二叉树

我们这里用链表来实现

typedef int TreeType;
typedef struct Tree
{
	TreeType a;
	struct Tree* left;
	struct Tree* right;
}Tree;

四. 二叉树的前中后序遍历

1. 前序遍历

先输出根节点再输出左子树再输出右子树

递归实现

void PrevOrder(Tree* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->a);//根
	PrevOrder(root->left);//左子树
	PrevOrder(root->right);//柚子树
}

非递归方法实现前序遍历,需要用到栈以及栈的插入,删除,初始化,判空等功能(中序,后序遍历同上)

void PrevOrder2(Tree* root)
{
	assert(root);
	ST s;
	StackInit(&s);
	Tree* p = root;
	while (p||!StackEmpty(&s))
	{
		if (p != NULL)
		{
			printf("%d  ", p->a);//输出根
			StackPush(&s,p);//将"根"存进去
			p = p->left;//往左子树走
		}
		else//左子树走到头
		{
			p = StackTop(&s);
			StackPop(&s);
			p = p->right;//若此节点还有右子树就执行上面的输出操作,若还是空就来继续pop节点
		}
	}
}
2. 中序遍历

先打印输出左子树,再输出根节点再输出右子树

递归实现:

void InOrder(Tree* root)//中序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);//左子树
	printf("%d ", root->a);//根
	InOrder(root->right);//柚子树
}

非递归实现:
 

void InOrder2(Tree* root)
{
	assert(root);
	ST s;
	StackInit(&s);
	Tree* p = root;
	while (p || !StackEmpty(&s))
	{
		if (p != NULL)
		{
			StackPush(&s, p);//先存节点,先进去的后出来
			p = p->left;//找最左边的节点
		}
		else//到最左边了
		{
			p = StackTop(&s);
			printf("%d  ", p->a);//输出最左边值
			StackPop(&s);
			p = p->right;//往右走看有没有节点
		}
	}
}
3. 后序遍历

先打印输出左子树,再右子树,再输出根节点

递归代码:

void PostOrder(Tree* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);//左子树
	PostOrder(root->right);//柚子树
	printf("%d ", root->a);//根
}

非递归代码:

void PostOrder2(Tree* root)
{
	assert(root);
	ST s;
	StackInit(&s);
	Tree* p = root;
	Tree* k = root;
	while (p || !StackEmpty(&s))
	{
		while (p != NULL)
		{
			StackPush(&s, p);
			p = p->left;//直接走到最左边
		}
		p = StackTop(&s);//
		if (p->right&&p->right!=k)//看是否有右子树
		{
			p = p->right;//往右走,循环开始后又会走到此节点的最左边
		}
		else
		{
			printf("%d  ", p->a);
			k = p;//将右子树记录下来,否则会死循环
			p = NULL;//将p置为空,否则循环开始会再次走到最左边
			StackPop(&s);
		}
	}
}
4.完整代码
typedef int TreeType;

typedef struct Tree
{
	TreeType a;
	struct Tree* left;
	struct Tree* right;
}Tree;

typedef Tree* StackType;


typedef struct Stack
{
	StackType* a;
	int Top;
	int capacity;
}ST,*pS;

void StackInit(pS pst)
{
	assert(pst);

	pst->a = NULL;
	pst->capacity = 0;
	pst->Top = 0;
}
void BuyNode(pS pst)
{

	int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
	StackType* tmp = (StackType*)realloc(pst->a, sizeof(StackType) * newcapacity);
	if (tmp == NULL)
	{
		perror("relloc fail");
		return;
	}
	pst->a = tmp;
	pst->capacity = newcapacity;
}
void StackPush(pS pst, StackType data)
{
	assert(pst);
	if (pst->Top == pst->capacity)
	{
		BuyNode(pst);
	}
	pst->a[pst->Top] = data;
	pst->Top++;
}

void StackPop(pS pst)
{
	assert(pst);

	pst->Top--;//数组里的值不用改,再添加数据会覆盖掉
}

StackType StackTop(pS pst)
{
	assert(pst);

	return pst->a[pst->Top - 1];
}

bool StackEmpty(pS pst)
{
	assert(pst);
	return pst->Top == 0;
}

void PrevOrder(Tree* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->a);//根
	PrevOrder(root->left);//左子树
	PrevOrder(root->right);//柚子树
}
void PrevOrder2(Tree* root)
{
	assert(root);
	ST s;
	StackInit(&s);
	Tree* p = root;
	while (p||!StackEmpty(&s))
	{
		if (p != NULL)
		{
			printf("%d  ", p->a);//输出根
			StackPush(&s,p);//将"根"存进去
			p = p->left;//往左子树走
		}
		else//左子树走到头
		{
			p = StackTop(&s);
			StackPop(&s);
			p = p->right;//若此节点还有右子树就执行上面的输出操作,若还是空就来继续pop节点
		}
	}
}
void InOrder(Tree* root)//中序遍历
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);//左子树
	printf("%d ", root->a);//根
	InOrder(root->right);//柚子树
}
void InOrder2(Tree* root)
{
	assert(root);
	ST s;
	StackInit(&s);
	Tree* p = root;
	while (p || !StackEmpty(&s))
	{
		if (p != NULL)
		{
			StackPush(&s, p);//先存节点,先进去的后出来
			p = p->left;//找最左边的节点
		}
		else//到最左边了
		{
			p = StackTop(&s);
			printf("%d  ", p->a);//输出最左边值
			StackPop(&s);
			p = p->right;//往右走看有没有节点
		}
	}
}
void PostOrder(Tree* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);//左子树
	PostOrder(root->right);//柚子树
	printf("%d ", root->a);//根
}
void PostOrder2(Tree* root)
{
	assert(root);
	ST s;
	StackInit(&s);
	Tree* p = root;
	Tree* k = root;
	while (p || !StackEmpty(&s))
	{
		while (p != NULL)
		{
			StackPush(&s, p);
			p = p->left;//直接走到最左边
		}
		p = StackTop(&s);//
		if (p->right&&p->right!=k)//看是否有右子树
		{
			p = p->right;//往右走,循环开始后又会走到此节点的最左边
		}
		else
		{
			printf("%d  ", p->a);
			k = p;//将右子树记录下来,否则会死循环
			p = NULL;//将p置为空,否则循环开始会再次走到最左边
			StackPop(&s);
		}
	}
}

int main()
{
	Tree* a = (Tree*)malloc(sizeof(Tree));
	a->a = 2;
	a->left = NULL;
	a->right = NULL;
	Tree* b = (Tree*)malloc(sizeof(Tree));
	b->a = 3;
	b->left = NULL;
	b->right = NULL;
	Tree* c = (Tree*)malloc(sizeof(Tree));
	c->a = 1;
	c->left = NULL;
	c->right = NULL;
	Tree* d = (Tree*)malloc(sizeof(Tree));
	d->a = 5;
	d->left = NULL;
	d->right = NULL;
	Tree* e = (Tree*)malloc(sizeof(Tree));
	e->a = 6;
	e->left = NULL;
	e->right = NULL;

	Tree* f = (Tree*)malloc(sizeof(Tree));
	f->a = 8;
	f->left = NULL;
	f->right = NULL;

	a->left = b;
	a->right = c;
	b->left = d;
	b->right = e;
	d->right = f;
	//PrevOrder2(a);
	//InOrder(a);
	//InOrder2(a);
	//PostOrder(a);
	PostOrder2(a);
	//int size = 0;
	//printf("%d ", size);
	//printf("\n%d \n", TreeSize(a));
	//printf("\n%d \n", TreeLeafSize(c));
	return 0;
}

这篇就到这里啦~

希望能给你带来一些帮助  (๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

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

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

相关文章

C 语言中如何实现图结构?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; &#x1f4d9;C 语言百万年薪修炼课程 【https://dwz.mosong.cc/cyyjc】通俗易懂&#xff0c;深入浅出&#xff0c;匠心打磨&#xff0c;死磕细节&#xff0c;6年迭代&…

基于docker-compose部署zabbix7.0

1.安装docker和docker-compose 已有可跳过&#xff0c;没有参照我的docker一件安装脚本连接放在下方 一键安装dockerv24.0.6以及docker-compose可离线_docker 24对应docker-compose-CSDN博客 2.运行zabbix-server 1.创建zabbix工作目录 mkdir /zabbix 2.编写docker-compos…

【人工智能】Transformers之Pipeline(一):音频分类(audio-classification)

​​​​​​​ 目录 一、引言 二、音频分类&#xff08;audio-classification&#xff09; 2.1 概述 2.2 技术原理 2.2.1 Wav2vec 2.0模型 2.2.1 HuBERT模型 2.3 pipeline参数 2.3.1 pipeline对象实例化参数 2.3.2 pipeline对象使用参数 2.4 pipeline实战 2.4.1 …

【python】Python中常见的KeyError报错分析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

ESP32FreeRTOS开发笔记:1.双核并行

ESP32 的 Arduino 框架内部集成了 FreeRTOS&#xff0c;允许开发者利用其多任务处理功能。在代码中&#xff0c;xTaskCreatePinnedToCore 函数是 FreeRTOS 提供的 API&#xff0c;用于创建任务并指定任务在哪个核心上运行。 FreeRTOS 是一个流行的实时操作系统内核&#xff0c;…

信息打点web篇--语言开发框架,组件识别

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本章节主要整理 识别语言开发框的打点内容 框架简介 高效理解:把用于做某些事的代码封装起来&#xff0c;使用者无需自己写代码直接一个函数就能完成本该很多行才能完成的功能。 例子:我们要写网站&#xff0c;…

Open3D 点云Kmeans聚类算法

目录 一、概述 1.1算法介绍 1.2实现步骤 二、代码实现 三、实现效果 3.1原始点云 3.2聚类后点云 前期试读&#xff0c;后续会将博客加入该专栏&#xff0c;欢迎订阅Open3D与点云深度学习的应用_白葵新的博客-CSDN博客 一、概述 1.1算法介绍 聚类是一种将数据集分组的方…

Qml 图片和加载器操作

学习目标&#xff1a;Qml 图片和加载器编程 学习前置 Qt Qml编程 基础部分 认识qml-CSDN博客 实现效果 对图片的基本操作 加载器 核心代码 加载器 思路&#xff1a; 创建一个加载器 默认是几个圆点&#xff0c;我们重写加载器元素&#xff08;contentItem&#xff09;&…

文献阅读:高效和稳健的 π-FISH rainbow 用于多种生物分子的多重原位检测

文献介绍 文献题目&#xff1a; Highly efficient and robust π-FISH rainbow for multiplexed in situ detection of diverse biomolecules 研究团队&#xff1a; 曹罡&#xff08;华中农业大学&#xff09;、戴金霞&#xff08;华中农业大学&#xff09; 发表时间&#xff…

RSA算法(C++)

RSA加解密过程 RSA为非对称加密算法&#xff0c;由一对公钥和一对私钥构成&#xff0c;私钥加密公钥解密&#xff0c;公钥加密私钥解密 如下图,D为私密的&#xff0c;假设传输英文字母&#xff0c;我们给英文字母编号A1,B2,C3… RSA加解密过程 两对密钥产生方法如下 C Op…

网络通信基本知识

网络通信 什么是网络通信&#xff1f; 通信网络是指将各个孤立的设备进行物理连接&#xff0c;实现人与人&#xff0c;人与计算机&#xff0c;计算机与计算机之间进行信息交换的链路&#xff0c;从而达到资源共享和通信的目的。 什么是网络协议&#xff1f; 网络协议是计算机…

Python函数 之 参数

1.参数的简单介绍 参数 形式参数(形参)&#xff1a;在函数定义的时候,在括号中写⼊变量,这个变量就称为是函数的参数。实际参数(实参)&#xff1a;在函数调⽤的时候,可以给定义时候的形参传递具体的数据值,供其使⽤。注&#xff1a; 在函数调⽤的时候&#xff0c;会将函数的实…

wps 将列的内容转换为一个单元格内容,并以逗号分隔

wps 将列的内容转换为一个单元格内容&#xff0c;并以逗号分隔 1.首先在一个空白单元格输入 2.输入函数TEXTJOIN 这个函数有三个参数&#xff0c;第一个&#xff1a;以什么分隔符分隔&#xff0c;第二个&#xff1a;是否忽略空白格&#xff0c;true:忽略 false:不忽略 第三个&…

怎么使用代理IP?如何检测代理IP是否有效?

代理IP是一种网络代理技术&#xff0c;它是通过中间服务器来转发网络请求的IP地址。当我们使用代理IP时&#xff0c;我们的真实IP地址会被隐藏起来&#xff0c;而代理服务器的IP地址会被用作我们的身份标识。使用代理IP的步骤如下&#xff1a; 1.选择合适的代理服务器 考虑服务…

【微信小程序知识点】转发功能的实现

转发功能&#xff0c;主要帮助用户更流畅地与好友分享内容与服务。 想实现转发功能&#xff0c;有两种方式&#xff1a; 1.页面js文件必须声明onShareAppMessage事件监听函数&#xff0c;并自定义转发内容。只有定义了此事件处理函数&#xff0c;右上角菜单才会显示“转发”按…

WebStorm 使用 ESLint 自动格式化代码

WebStorm 不能像 VSCode 那样在保存的时候自动 Fix-ESLint&#xff0c;不能自动格式化代码&#xff0c;需要安装一个插件 安装 ESLint 插件 进入设置快捷键 win&#xff1a;CtrlAltS mac: command, 找到 Plugins&#xff0c;搜索eslint 在这里插入图片描述 安装后配置一下 …

【通信协议-RTCM】MSM语句(1) - 多信号GNSS观测数据消息格式

注释&#xff1a; RTCM响应消息1020为GLONASS星历信息&#xff0c;暂不介绍&#xff0c;前公司暂未研发RTCM消息类型版本的DR/RTK模块&#xff0c;DR/RTK模块仅NMEA消息类型使用 注释&#xff1a; 公司使用的多信号语句类型为MSM4&MSM7&#xff0c;也应该是运用最广泛的语句…

从新手到进阶:高效设计 Tableau 可视化的 5 种技巧 | 数据可视化分析

让我们一起跟着大神学习五个超实用的技巧&#xff0c;加速你的可视化分析之旅&#xff01; 在日常分析中&#xff0c;人人都想实现可视化最佳实践。然而&#xff0c;对于很多初学者来说&#xff0c;在还未熟练掌握 Tableau 的情况下&#xff0c;这种愿望貌似不太符合实际。 为…

【想心静?】红尘中修炼的功夫,才是真正的功夫

刘君亮想要去山中静坐&#xff0c;先生说&#xff1a; 你若是以厌弃身外事物的心去静中寻求天理&#xff0c;反而只会养出骄傲怠惰的习气。你若能不厌弃身外事物&#xff0c;然后再到静处涵养天理&#xff0c;却是好的。 去一个安静的地方&#xff0c;去沉静一下自己的内心也…

电子画册制作流程技巧大揭秘

​随着科技的发展&#xff0c;电子画册已经逐渐取代传统纸质画册&#xff0c;成为展示企业形象、传播信息的重要工具。一份精美的电子画册&#xff0c;不仅能提升企业品牌形象&#xff0c;还能吸引更多潜在客户。那么&#xff0c;如何制作一份既精美又实用的电子画册呢&#xf…