数据结构:树详解

news2024/11/19 0:50:53

创建二叉树

给出了完整的先序遍历序列,子树为空用’#’表示,所以这样我们在通过先序遍历序列创建二叉树时我们直到先序遍历序列是先进行根结点,然后左子树最后右子树的顺序进行遍历的,所以对于完整的先序遍历序列我们可以直到先序遍历序列中第一个元素是二叉树的根结点,如果第二个元素不为’#’,那么这个代表二叉树有左孩子,而且左孩子的值为先序遍历序列的第二个元素的值,依次类推,根据二叉树的完整先序遍历序列我们可以直到每一个结点是否为空,这样我们就能够采取递归形式进行二叉树的创建:
创建过程的图示为如下:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

最终我们得到的树如下图所示:
在这里插入图片描述

有了上面的思路我们可以写出如下代码:

void InitBinaryTree(char *p, int *length, struct BinaryTreeNode **root){
	if(p[*length]!=0){
		if(p[*length]!='#'){
			*root = (struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
			(*root)->val = p[*length];
			(*root)->left = NULL;
			(*root)->right = NULL;
			(*length)++;
			InitBinaryTree(p, length, &(*root)->left);
			InitBinaryTree(p, length, &(*root)->right);	
		}else{
			(*length)++;
		}
	}
}

这就是通过树的完整前序遍历序列创建二叉树的过程。
下面我们来进行实现二叉树的前序遍历、中序遍历与后序遍历,前序遍历是指先根结点再左子树最后右子树,中序遍历是先左子树然后根结点最后右子树,后序遍历是先左子树然后右子树最后根结点,这种遍历可以通过递归进行实现,在每次递归中所在结点不为NULL就说明结点有值,我们需要遍历这一个结点的左子树与右子树,也就是递归截至的条件是root==NULL;有了这样的思路前序遍历与中序遍历,与后序遍历就只是根结点的访问顺序发生改变,我们可以写出下面的三种遍历的代码:
前序遍历:

//本函数实现二叉树的前序遍历功能
void preOrderTraversal(struct BinaryTreeNode *root){
	if(root!=NULL){
		printf("%c ", root->val);
		preOrderTraversal(root->left);
		preOrderTraversal(root->right);	
	}
}

中序遍历:

//本函数实现二叉树的中序遍历功能
void inOrderTraversal(struct BinaryTreeNode *root){
	if(root!=NULL){
		inOrderTraversal(root->left);
		printf("%c ", root->val);
		inOrderTraversal(root->right);	
	}
}

后序遍历:

// 本函数实现二叉树的后序遍历功能
void postOrderTraversal(struct BinaryTreeNode *root){
	if(root!=NULL){
		postOrderTraversal(root->left);
		postOrderTraversal(root->right);
		printf("%c ", root->val);	
	}
}

就以上面的建立的二叉树为例查看一下该二叉树的前序遍历序列、中序遍历序列、后序遍历序列。
二叉树的前序遍历序列应为:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

中序遍历序列应为:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

后序遍历序列应为:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们来运行一下看看结果是否正确:
在这里插入图片描述
可以看出结果确实正确。
至此关于二叉树的内容已经全部实现,接下来实现哈夫曼树以及编码操作,题目中给出了’a’ ‘b’ ‘c’ ‘d’以及其对应出现的次数分别是7、5、2、4,出现次数多的字母编码要尽可能的短,我们可以利用哈夫曼树来实现对应的操作,哈夫曼树是为每个结点增加一个权值,权值大的结点离根要尽可能的近,我们就可以将所有的字符视作只有一个根结点的树,将结点权值小的两个子树进行合成组成一颗新树,两个子树的权值之和作为新树的权值,这一颗新树与剩余的所有树组成一个新的集合,然后从中选取两个树进行上述过程,如此重复下去,直到剩下一棵树,这棵树就是哈夫曼树。图示如下:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

如此进行下去最后只剩下一棵树:
在这里插入图片描述

这就是哈夫曼树,所以只需要将字母出现的次数作为权值,按照上述规则我们就能够生成一颗哈夫曼树。代码如下:

#include<stdio.h>
#include<stdlib.h>
struct BinaryTreeNode{
	int weight;
	char val;
	struct BinaryTreeNode * left;
	struct BinaryTreeNode * right;
};
//对子树按照权值进行降序排列,这样只操作最后面的两棵树就行了
void sort(struct BinaryTreeNode ** p, int length){
	for(int i=0;i<length-1;i++){
		for(int j=0; j<length-1; j++){
			if(p[j]->weight<p[j+1]->weight){
				struct BinaryTreeNode * t = p[j];
				p[j] = p[j+1];
				p[j+1] = t;
			}
		}
	}
}
//创建哈夫曼树
struct BinaryTreeNode * InitHuffmanTree(struct BinaryTreeNode ** p, int length){
	struct BinaryTreeNode * root = (struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
	while(length!=2){
		sort(p, length);
		root->left = (struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
        *(root->left) = *p[length-2];
		root->right = (struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
        *(root->right) = *p[length-1];
        root->val = 0;
		root->weight = p[length-2]->weight+p[length-1]->weight;
        free(p[length-1]);
        free(p[length-2]);
		p[length-2] = root;
		root = (struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
		length -= 1;
	}
	root->left = p[length-2];
	root->right = p[length-1];
	root->weight = p[length-2]->weight+p[length-1]->weight;
	return root;
}
//前序遍历
void preOrderTraversal(struct BinaryTreeNode *root){
	if(root!=NULL){
		printf("%d ", root->weight);
		preOrderTraversal(root->left);
		preOrderTraversal(root->right);	
	}
}
//中序遍历
void inOrderTraversal(struct BinaryTreeNode *root){
	if(root!=NULL){
		inOrderTraversal(root->left);
		printf("%d ", root->weight);
		inOrderTraversal(root->right);	
	}
}
int main(){
	int n;
	char code[4][20];
	printf("请问你有多少个字符需要进行编码:");
	scanf("%d", &n);
	struct BinaryTreeNode ** p = (struct BinaryTreeNode **)malloc(sizeof(int)*n);
	printf("请按顺序输入字符与其出现的次数。\n");
	for(int i=0; i<n; i++){
		p[i] = (struct BinaryTreeNode *)malloc(sizeof(struct BinaryTreeNode));
		p[i]->left = NULL;
		p[i]->right = NULL;
		printf("请输入字符:");
		getchar();
		scanf("%c", &p[i]->val);
		getchar();
		printf("请输入字符出现的次数:");
		scanf("%d", &p[i]->weight);
	}
	printf("请确认你刚才输入的信息:\n");
	for(int i=0; i<n; i++){
		printf("字符%c,出现%d次\n", p[i]->val, p[i]->weight);
	}
	struct BinaryTreeNode * root = InitHuffmanTree(p, n);
	printf("此哈夫曼树的权值前序序列为:");
	preOrderTraversal(root);
	printf("\n此哈夫曼树的中序序列为:");
	inOrderTraversal(root);
	return 0;
}

在这里插入图片描述

根据权值的前序序列与中序序列可以建立如下二叉树:
在这里插入图片描述

因为出现在叶子结点的才是字符,所以我们可以直到权值为7的结点时’a’,权值为4的结点是’d’,权值为2的结点是’c’,权值为5的结点时’b’,即如下图所示:
在这里插入图片描述

按照哈夫曼树的建立规则进行手动建树,可以建出以下这个树:
在这里插入图片描述

可以看出这两棵树是等价的,只不过是左右孩子交换了下顺序,也就是说明了程序是正确的。接下来就是进行哈夫曼编码了。
向左为0,向右为1进行编码,进行二进制编码,可以写出下面的代码:

void HuffmanCode(struct BinaryTreeNode *root, char n[20], char p[][20], int length){
	//p用来存储编好的编码
	if(root!=NULL){
		switch(root->val){
			case 'a':	
			case 'b':
			case 'c':
			case 'd':
				int i;
				for(i=0;i<length;i++){
					p[root->val-'a'][i]=n[i];
				}
				p[root->val-'a'][i]='\0';
		}
		n[length++] = '0';
		n[length] = 0;
		HuffmanCode(root->left, n, p, length);
		length--;
		n[length++] = '1';
		n[length] = 0;
		HuffmanCode(root->right, n, p,length);
	}
}

对上面的树进行编码
在这里插入图片描述

运行结果:
在这里插入图片描述

可以看到确实生成了哈夫曼编码。

如果有什么地方讲的不好或者讲错的地方欢迎大家指出来,如果我所讲的对你们有帮助不要忘了点赞、收藏、关注哦!


我是你们的好伙伴apprentice_eye


一个致力于让知识变的易懂的博主。

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

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

相关文章

指定日期D的年份和月份返回日期D所在的月份:(1)第一天是星期几?(2)该月共有多少天?calendar.monthrange(year,month)

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 指定日期D的年份和月份 返回日期D所在的月份&#xff1a; &#xff08;1&#xff09;第一天是星期几&#xff1f; &#xff08;2&#xff09;该月共有多少天&#xff1f; calendar.monthrange(…

CMU15-445-Spring-2023-Project #1 - 前置知识(lec01-06)

Lecture #01_ Relational Model & Relational Algebra Databases 数据库是相互关联的数据的有组织集合&#xff0c;对现实世界的某些方面进行建模。区别于DBMS&#xff08;MySQL、Oracle&#xff09;。 Flat File Strawman 数据库以CSV文件的形式存储&#xff0c;并由D…

梦想家内容管理系统(Dreamer CMS)跨站请求伪造漏洞

梦想家内容管理系统&#xff08;Dreamer CMS&#xff09;跨站请求伪造漏洞 目标:GitHub - iteachyou-wjn/dreamer_cms: Dreamer CMS 梦想家内容发布系统采用流行的SpringBoot搭建&#xff0c;支持静态化、标签化建站。不需要专业的后台开发技能&#xff0c;会HTML就能建站&…

真核微生物基因组质量评估工具EukCC的安装和详细使用方法

介绍&#xff1a; GitHub - EBI-Metagenomics/EukCC: Tool to estimate genome quality of microbial eukaryotes 安装&#xff1a; docker&#xff1a; docker pull microbiomeinformatics/eukcc 推荐conda 环境&#xff1a; conda install -c conda-forge -c bioconda …

Jenkins修改全局maven配置后不生效解决办法、以及任务读取不同的settings.xml文件配置

一、修改Global Tool Configuration的maven配置不生效 说明&#xff1a;搭建好jenkins后&#xff0c;修改了全局的settings.xml&#xff0c;导致读取settings一直是之前配置的。 解决办法一 Jenkins在创建工作任务时&#xff0c;会读取当前配置文件内容&#xff0c;固定在这…

Spring+Vue实战项目环境准备跑通程序

SpringVue 源项目 后端&#xff1a; 首先在GitHub上克隆项目到本地&#xff08;zip包下载/sourcetree拉取/gitbash克隆&#xff09;。 https://github.com/songboriceman/doubao_community_backend 然后下载Lombok安装到你的IDE&#xff08;eclipse&#xff09;。 https://p…

【uniapp】APP打包上架应用商-注意事项

初雪云-uniapp启动图自定义生成&#xff08;支持一键生成storyboard&#xff09; 一、修改App端上传图片/视频 uni.uploadFile let thatthis; uni.chooseImage({count: 1,sourceType: [camera,album],sizeType: [compressed, original],success: rey > {uni.showLoading({ t…

VS code的使用介绍

VS code的使用介绍 简介下载和安装常用的插件使用教程快捷键 集成Git未找到 Git。请安装 Git&#xff0c;或在 "git.path" 设置中配置。操作步骤打开文件夹初始化仓库文件版本控制状态提交文件到git打开git操作栏位 好用的插件ChineseDraw.io Integration实体关系 Gi…

案例099:基于微信小程序的外卖小程序的研究与开发

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

leetcode 每日一题 2023年12月30日 一周中的第几天

题目 给你一个日期&#xff0c;请你设计一个算法来判断它是对应一周中的哪一天。 输入为三个整数&#xff1a;day、month 和 year&#xff0c;分别表示日、月、年。 您返回的结果必须是这几个值中的一个 {"Sunday", "Monday", "Tuesday", &qu…

数字图像处理 Harris 角点和边缘检测器

一、简述 Harris角点和边缘检测器是一项古老的技术,说它古老是因为该技术从1988年被发明。下面是论文地址,技术出现的时间虽然很久,但是并不代表没有用处了,很多神经网络无法发挥作用的场景下,类似的技术还是会大行其道。 https://web.stanford.edu/class/cs231m/referen…

通过聚道云软件连接器实现钉钉与自研主数据系统的完美融合

客户介绍 某知名高校&#xff0c;拥有数千名教职工&#xff0c;日常管理涉及大量的人员异动信息。该高校设有多个学院和研究所&#xff0c;涵盖了工、理、管、文等多个学科领域。该高校是一所充满活力和潜力的学府&#xff0c;致力于为学生提供优质的教育资源和多元化的学习环…

大型语言模型的幻觉问题

1.什么是大模型幻觉&#xff1f; 在语言模型的背景下&#xff0c;幻觉指的是一本正经的胡说八道&#xff1a;看似流畅自然的表述&#xff0c;实则不符合事实或者是错误的。 幻觉现象的存在严重影响LLM应用的可靠性&#xff0c;本文将探讨大型语言模型(LLMs)的幻觉问题&#x…

[嵌入式C][入门篇] 快速掌握基础2 (数据类型、常量、变量)

开发环境&#xff1a; 网页版&#xff1a;跳转本地开发(Vscode)&#xff1a;跳转 文章目录 一、基本变量大小和范围&#xff08;1&#xff09;在8位/32位单⽚机中&#xff1a;测试代码结果&#xff1a;64位机器结果&#xff1a;32位机器&#xff08;单片机&#xff09;无对齐限…

Web爬虫中CAPTCHA挑战的解决方法

CAPTCHA是网站用来区分人类用户和自动机器人的一种安全措施。它包括向用户提出一些挑战&#xff0c;如扭曲的文本、图像或拼图&#xff0c;用户必须解决以证明其真实性。然而&#xff0c;在Web爬取过程中&#xff0c;遇到验证码可能会带来相当大的挑战。本文将探讨在Web爬虫过程…

使用 C# Winfrom编写倒计时功能

在日常生活中&#xff0c;我们经常需要倒计时来提醒自己重要的时间节点&#xff0c;比如倒计时到达一个特定的日期和时间。介绍一个使用 C# 编写的倒计时应用程序的实现。 步骤一&#xff1a;应用程序的功能 它具有以下几个主要特点&#xff1a; 用户输入目标日期和时间&…

Wrk压测发送Post请求的正确姿势

一、Wrk简介 wrk 是一个能够在单个多核 CPU 上产生显著负载的现代 HTTP 基准测试工具。它采用了多线程设计&#xff0c;并使用了像 epoll 和 kqueue 这样的可扩展事件通知机制。此外&#xff0c;用户可以指定 LuaJIT 脚本来完成 HTTP 请求生成、响应处理和自定义报告等功能。 …

个人笔记:分布式大数据技术原理(二)构建在 Hadoop 框架之上的 Hive 与 Impala

有了 MapReduce&#xff0c;Tez 和 Spark 之后&#xff0c;程序员发现&#xff0c;MapReduce 的程序写起来真麻烦。他们希望简化这个过程。这就好比你有了汇编语言&#xff0c;虽然你几乎什么都能干了&#xff0c;但是你还是觉得繁琐。你希望有个更高层更抽象的语言层来描述算法…

1-01初识C语言

一、概述 C语言是贝尔实验室的Ken Thompson&#xff08;肯汤普逊&#xff09;、Dennis Ritchie&#xff08;丹尼斯里奇&#xff09;等人开发的UNIX 操作系统的“副产品”&#xff0c;诞生于1970年代初。 Thompson和Ritchie共同创作完成了Unix操作系统&#xff0c;他们都被称为…

操作系统----期末单选题

目录 操作系统----期末单选题 一、单选题 操作系统----期末单选题 一、单选题 1&#xff0e; 采用分段存储管理的系统中&#xff0c;若地址用24位表示&#xff0c;其中8位表示段内地址&#xff0c;则允许分段的最大个数是&#xff08; &#xff09;。 A、224 B、216 …