数据结构之树的超详细讲解(附C实现代码)

目录

树的基本性质

二叉树

定义树结点结构体

 建树

根据二叉树的层次遍历建树

根据前序或后序建树

遍历二叉树

前序遍历

中序遍历

后序遍历

根据前序和中序序列输出后序序列

根据后序和中序序列输出前序序列

根据前序和后序判断树的个数

求树的高度(DFS)

求树的宽度

求树的叶子结点个数

哈夫曼树

二叉排序树

树的基本性质

节点的度:该节点拥有的孩子个数

叶子节点:度为0的节点

层数:根节点为第一层,根的子节点为第二层,以此类推

所有树的性质:所有节点的总度数等于节点数减一

完全m 叉树性质

完全m 叉树,节点的度数最大为m ,且必须按照从上到下从左到右的顺序依次填满,叶子节点只出现在倒数第一层和倒数第二层。

常用性质如下

(1)第i 层最多拥有的节点个数为

(2)d 层的完全m 叉树最多拥有的节点个数为

假设完全m 叉树有n 个节点

(3) n 个结点的完全m 叉树的深度为 , ceil 表示向上取整

(4) 当i<d 时,第i 层节点个数为

d 层的节点个数为

(5)在满m 叉树中,一个编号为p 的结点(编号从1开始),它的深度是 ,它的父节点编号为 ,易得该结点的第k-1 个孩子的编号为p*k ,所以推导出该结点的第i 个孩子的编号为  

(6)对于n 个结点的二叉树,按从上到下,从左到右依次给结点编号

i 的双亲结点为i//2 ,若n<2i ,则i 是叶子结点,若n≥2i ,则其左孩子为2i ,若n≥2i+1 ,则其右孩子是结点2i+1 ,若n<2i+1 ,则其无左孩子

(7) n 个结点的不同二叉树有

二叉树

定义树结点结构体

typedef struct BiNode{	
	char data;    //数据域
	struct BiNode *lchild;	//定义lchild变量为指向Node结构体的指针,指向左子树根结点 
	struct BiNode *rchild;	//rchild指向右子树根结点 
}BiNode;

 建树

根据二叉树的层次遍历建树

空的结点用#表示

BiNode *CreateTree(char a[], int size, int index){	 
	if(index >= size || a[index] == '#')	//如果下标超过或者本来该结点为空 
		return NULL;
	BiNode *node = (BiNode* )malloc(sizeof(BiNode));//给node结点分配对应Node结构体的内存空间,然后将malloc返回的指针转换成Node 
	node->data = a[index];
	node->lchild = CreateTree(a, size, 2*index+1);	//创建node结点的左子树 
	node->rchild = CreateTree(a, size, 2*index+2);	//创建node结点的右子树
	//数组下标从0开始,所以2i+1是左孩子,2i+2是右孩子 
	//如果是从1开始的话,2i是左孩子,2i+1是右孩子
	return node;
}

 主函数调用

int n[]={'A','B','C','#','D' ,'E','#','F'};	
//二叉树结点的数组表示,#表示该结点为空 
BiNode *tree=CreateTree(n, 8 , 0);	//创建二叉树tree,该树实质是一个结点,该结点直接或间接指向其它结点

根据前序或后序建树

已知二叉树的前序或后序或中序其中一个,其中空的节点要特别指出来,不然无法唯一确定一个树

例如先序为ABC##DE#G##F###

根节点是 A。接下来是 B,是 A 的左子节点。接下来是 C,是 B 的左子节点。两个 #,表明 C 的左右子节点为空。接下来是 D,是 B 的右子节点。接下来是 E,是 D 的左子节点。一个 #,表明 E 的左子节点为空。接下来是 G,是 E 的右子节点。两个 #,表明 G 的左右子节点为空。接下来是 F,是 D 的右子节点。三个 #,表明 F 的左右子节点以及 D 的右子节点为空。

已知前序序列a[]建树

BiNode *CreateTree(char a[],int size) {
	if (i>=size) 
		return NULL;
	if (a[i] == '#'){
		i++;
		return NULL;
	}
	
	BiNode *node = (BiNode*)malloc(sizeof(BiNode));	
	//malloc先新生成一个BiNode结点,然后用(BiNode*)强制转换为指向BiNode类型的指针
//根左右
	node->data = a[i++];	
	node->lchild = CreateTree(a,size);
	node->rchild = CreateTree(a,size);
	return node;
}

ps:只知道中序是不能唯一确定一个树的

遍历二叉树

前序遍历

void PreOrder(BiNode* root){//先序遍历,根左右 
	if(root==NULL)
		return;
	printf("%c ",root->data);
	PreOrder(root->lchild);
	PreOrder(root->rchild);
}

中序遍历

void InOrder(BiNode* root){//中序遍历,左根右 
	if(root==NULL)
		return;
	InOrder(root->lchild);
	printf("%c ",root->data);
	InOrder(root->rchild);
}

后序遍历

void PostOrder(BiNode* root){//后序遍历,左右根 
	if(root==NULL)
		return;
	PostOrder(root->lchild);
	PostOrder(root->rchild);
	printf("%c ",root->data);
}

根据前序和中序序列输出后序序列

//全局变量
char pre[100] //前序序列
char in[100]; //后序序列
void build(int root,int start,int end){
	if(start>end)
		return;
	int i=start;
	while(in[i]!=pre[root])
		i++;
	build(root+1,start,i-1);
	build(root+i-start+1,i+1,end);
	printf("%c",pre[root]);
}
//主函数调用
build(0,0,strlen(pre)-1);

例如

先序:A B C D E F G H I J

中序:C D B F E A I H G J

一开始pre[root]是A,遍历in找到A,i指向A停下,这时A左右有两部分,CDBFE为左子树,范围为start到i-1,由先序序列得该树根为B,即root+1。IHGJ为右子树,范围为i+1到end,由先序序列得该树根为G,即root+i-start+1,依次类推。

根据后序和中序序列输出前序序列

同理可以根据后序和中序序列输出前序序列

// 全局变量
char post[100]; // 后序序列
char in[100];   // 中序序列
void build(int root, int start, int end) {
    if (start > end)
        return;
    // 在中序序列中找到根节点的位置
    int i = start;
    while (in[i] != post[root])
        i++;
    // 根节点在前序序列中的位置
    printf("%c", post[root]);
    // 递归构建左子树和右子树
    // 左子树的根节点在后序序列中的位置是 root - (end - i) - 1
    build(root - (end - i) - 1, start, i - 1);
    // 右子树的根节点在后序序列中的位置是 root - 1
    build(root - 1, i + 1, end);
}

根据前序和后序判断树的个数

知道前序和后序是无法唯一确定一个树的,但是可以知道这样的树有多少个,如果一个结点只有一个孩子,那么这个孩子可以是它的左孩子或右孩子,如果这样的结点有n个,则树有2^n个

先定义一个寻找索引函数,用于返回一个数在数组的索引

int findIndex(char arr[], int size, int value) { 
    for (int i=0; i<size; i++){
        if (arr[i]==value){
            return i;
        }
    }
    return -1;
}

有这样的性质:对于一个结点,它在后序的索引为Index,它的先序下一个结点在后序的索引为Indexnext,如果Indexnext=Index,则该结点只有一个孩子 

计算只有一个孩子节结点有多少个

int fun(char pre[], char post[], int size) {
    int count = 0;
	int i;
    for (i=0; i<size-1; i++) {//最后一个节点没有下一个节点,所以不用检查 
        int PreIndex=findIndex(post,size,pre[i]);
        int nextPreIndex=findIndex(post,size,pre[i+1]);
        if (PreIndex==nextPreIndex+1){
            count++;
        }
	return count
}

最后输出2^count次方就是答案 

求树的高度(DFS)

res参数保存当前的高度,如果大于deepth就让deepth等于res,最后deepth就会是最大高度

void dfs(BiNode *root,int res){
    if(root==NULL)
        return;
    res=res+1;
    if(root->lchild==NULL && root->rchild==NULL){
        if(res>deepth)
            deepth=res;	// deepth是全局变量
    }
    dfs(root->lchild,res);    //递归左子树
    dfs(root->rchild,res);    //递归右子树
}

//主函数调用
char a[]={'A','B','C','D','#','E','F','#','G'};
BiNode *root;
root=CreateTree(a,9,0);    //根据层次遍历建树
dfs(root,0);    //从0开始

求树的宽度

树的宽度是一层中结点数的最大值,定义一个nodenum数组用来存每层的结点数量,nodenum[i]的值表示第i+1层的结点数

void dfs(BiNode *root,int nodenum[],int h){
    if(root!=NULL){
        nodenum[h]++;    //nodenum[]是全局变量
        dfs(root->lchild,nodenum,h+1);
        dfs(root->rchild,nodenum,h+1);
    }
}

最后输出 nodenum数组的最大值就是树的宽度

求树的叶子结点个数

简单的dfs

void dfs(BiNode *root){
    if(root==NULL)
        return;
    if(root->lchild==NULL && root->rchild==NULL)//如果左孩子和右孩子都为空则为叶子结点
        leaf++;	//leaf是全局变量
    dfs(root->lchild);
    dfs(root->rchild);
}

哈夫曼树

求哈夫曼树的WPL(带权路径长度)

例如有五个结点,它们的权值分别为1 2 2 5 9

先对所有结点进行降序排序9 5 2 2 1

选最后两个数即最小的两个数,记录它们和为3,然后加到总和WPL,把倒数第二个数赋值为3

即9 5 2 3 1,最后一个数不用管,即9 5 2 3,然后再进行降序排序,依次类推,需要n-1轮

int WPL=0;
for(i=n-1;i>0;i--){
    Sort(a,i+1); 	//降序排序,对前i+1个数进行排序
    WPL=WPL+a[i]+a[i-1];
    a[i-1]=a[i]+a[i-1];
}
printf("%d\n",WPL);

Sort函数要自己实现,可以用最简单的冒泡排序 

二叉排序树

二叉排序树是特殊的二叉树,每个结点的都满足:左孩子比父结点小,右孩子比父节点大

建立二叉排序树

BiNode* createTree(BiNode *node, int x) {//接受一个树的根节点指针和一个要插入的值
    //建树
    if (node==NULL) {
        node=(BiNode*)malloc(sizeof(BiNode));
        node->data = x;
        node->lchild = NULL;
        node->rchild = NULL;
    }
    //插入值
    else if (x>node->data) {    //如果比该结点大就插入为右孩子
        node->rchild = createTree(node->rchild, x);
    }
    else if (x<node->data) {    //如果比该结点小就插入为左孩子
        node->lchild = createTree(node->child, x);
    }
    return node;
}

主函数调用

BiNode *root = NULL;
root = createTree(root, 10);
root = createTree(root, 5);
root = createTree(root, 13);
root = createTree(root, 14);
root = createTree(root, 19);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记——动态路由——RIP(RIP路由汇总介绍)

四、RIP路由汇总介绍 当网络中路由器的路由条目非常多时&#xff0c;可以通过路由汇总&#xff08;又称路由汇聚或路由聚合&#xff09;来减少路由条目数&#xff0c;加快路由收敛时间和增强网络稳定性。 路由汇总的原理是&#xff0c;同一个自然网段内的不同子网的路由在向外…

使用语义熵检测大语言模型中的幻觉

使用语义熵检测大语言模型中的幻觉 Detecting hallucinations in large language models using semantic entropy 论文阅读摘要研究目标论文图表概述总结关键解决方案语义熵计算:虚构内容检测: 双向蕴涵在大语言模型中的应用上下文的重要性蕴涵估计器 实验设计语义熵计算步骤结…

[每周尝鲜]用GPTs排名全球Top1的 GitHub 代码仓库分析神器AI Code Analyzer解读每周热门项目

前言&#xff1a; GitHub 代码仓库分析神器AI Code Analyzer自1月12日在GPTs 上线以来&#xff0c;凭借其强大的功能和卓越的用户体验&#xff0c;取得了令人瞩目的成绩。收获了诸多好评&#xff0c;目前在同类插件中全球排行第一&#xff0c;已有1000用户正在使用。并且已入选…

自动化运维Ansible

目录 一、Ansible介绍 1.1 功能 1.2 特性 二、Ansible安装 2.1 yum安装 2.2 编译安装 2.3 相关文件 三、 Ansible配置和工具 3.1 主配置文件 3.2 inventory主机清单文件 3.3 ansible工具 3.4 ansible命令 3.5 ansible执行过程 四、Ansible模块 4.1 command模块 4…

Python (Ansbile)脚本高效批量管理服务器和安全

1、简介 在现代 IT 基础设施中&#xff0c;管理大量服务器是一项复杂而繁琐的任务。特别是在检查服务器的存活状态以及 SSH 登录等任务上&#xff0c;手动操作非常耗时且容易出错。本文将介绍如何使用 Python 脚本实现对多台服务器的批量检查和管理&#xff0c;包括检查服务器…

TCP、UDP详解

TCP和UDP是传输层的两个重要协议&#xff0c;也是面试中经常会被问到的&#xff0c;属于面试高频点。今天&#xff0c;我们来学习这两个协议。 1.区别 1.1 概括 TCP&#xff1a;有连接&#xff0c;可靠传输&#xff0c;面向字节流&#xff0c;全双工 UDP&#xff1a;无连接…

clip系列改进Lseg、 group ViT、ViLD、Glip

Lseg 在clip后面加一个分割head&#xff0c;然后用分割数据集有监督训练。textencoder使用clip&#xff0c;frozen住。 group ViT 与Lseg不同&#xff0c;借鉴了clip做了真正的无监督学习。 具体的通过group block来做的。使用学习的N个group token&#xff08;可以理解为聚类…

探索音频创作的无限可能——Studio One 5 软件深度解析

Studio One 5 是一款功能强大且备受赞誉的音频制作软件&#xff0c;无论是专业音乐制作人还是业余爱好者&#xff0c;都能在其中找到满足自己需求的强大功能。 对于 Mac 和 Windows 用户来说&#xff0c;Studio One 5 提供了一个直观且友好的操作界面。其简洁明了的布局让用户…

CID引流电商:传统电商破局的新动力

摘要&#xff1a;CID引流电商为传统电商带来破局新机遇&#xff0c;通过跨平台引流、精准定位和高效转化&#xff0c;解决了流量获取难、成本高的问题&#xff0c;提升了销售业绩和市场竞争力。CID引流电商助力传统电商在激烈竞争中保持领先&#xff0c;推动行业持续发展。 随…

pdf转换成cad,这几个cad转换小妙招快码住!

在数字设计领域&#xff0c;PDF&#xff08;Portable Document Format&#xff09;和CAD&#xff08;Computer-Aided Design&#xff09;文件格式各有其独特之处。PDF常用于文件共享和打印&#xff0c;而CAD则是工程师和设计师们进行精确绘图和建模的必备工具。然而&#xff0c…

elasticsearch重置密码

0 案例背景 Elasticsearch三台集群环境&#xff0c;对外端口为6200&#xff0c;忘记elasticsearch密码&#xff0c;进行重置操作 注&#xff1a;若无特殊说明&#xff0c;三台服务器均需进行处理操作 1 停止es /rpa/bin/elasticsearch.sh stop 检查状态 ps -ef|grep elast…

基于PHP+MySQL组合开发家政预约服务小程序源码系统 带完整的安装代码包以及搭建教程

系统概述 在当今数字化时代&#xff0c;家政服务行业也逐渐融入了科技的力量。为了满足市场需求&#xff0c;我们开发了一款基于 PHPMySQL 组合的家政预约服务小程序源码系统。该系统不仅提供了便捷的家政服务预约功能&#xff0c;还具备完整的安装代码包和详细的搭建教程&…

OpenCloudOS开源的操作系统

OpenCloudOS 是一款开源的操作系统&#xff0c;致力于提供高性能、稳定和安全的操作系统环境&#xff0c;以满足现代计算和应用程序的需求。它结合了现代操作系统设计的最新技术和实践&#xff0c;为开发者和企业提供了一个强大的平台。本文将详细介绍 OpenCloudOS 的背景、特性…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的登山之旅01(100分)- 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

WPF----进度条ProgressBar(渐变色)

ProgressBar 是一种用于指示进程或任务的进度的控件&#xff0c;通常在图形用户界面&#xff08;GUI&#xff09;中使用。它提供了一种视觉反馈&#xff0c;显示任务的完成程度&#xff0c;帮助用户了解任务的进展情况。 基本特性 Minimum 和 Maximum 属性&#xff1a; 这些属…

游戏爱好者将《超级马里奥64》移植到GBA掌机

GBA虽然在当年拥有多款马里奥系列游戏&#xff0c;不过你一定没有想到&#xff0c;N64的《超级马里奥64》也能被移植到这个游戏掌机。近日&#xff0c;一位名为Joshua Barretto的开发者就完成了这一挑战。 大家都知道&#xff0c;《超级马里奥64》于1996年登陆任天堂64主机&am…

maven仓库的作用以及安装 , DEA配置本地Maven

ay12-maven 主要内容 Maven的作用Maven仓库的作用Maven的坐标概念Maven的安装IDEA配置本地Maven 一、maven概述 1.1、项目开发中的问题 1、我的项目依赖一些jar包&#xff0c;我把他们放在哪里&#xff1f;直接拷贝到项目的lib文件夹中?如果我开发的第二个项目还是需要上面…

VR加密方案常见问题有哪些?

在数字化时代&#xff0c;随着虚拟现实&#xff08;VR&#xff09;技术的迅速发展与普及&#xff0c;VR视频内容的安全传输成为关注焦点。为保护版权及敏感信息免遭非法复制或篡改&#xff0c;VR视频加密技术显得尤为重要。 首先&#xff0c;高效的加密算法对确保数据安全性至关…

java注解的概念及其使用方法详细介绍

1_注解&#xff1a;概述 路径 什么是注解注解的作用 注解 什么是注解&#xff1f; 注解(Annotation)也称为元数据&#xff0c;是一种代码级别的说明注解是JDK1.5版本引入的一个特性&#xff0c;和类、接口是在同一个层次注解可以声明在包、类、字段、方法、局部变量、方法参…

龙国南方航空滑块acw_v2+cookie+风控处理+type后缀

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未经许可禁…