IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构哈夫曼树的基本操作(C语言) -> 正文阅读

[数据结构与算法]数据结构哈夫曼树的基本操作(C语言)

目录

//1、树的结构定义

//2、创建哈夫曼树

//3、 根到叶子的路径

?//4、哈夫曼编码

//完整测试源码


//1、树的结构定义

#define n 4	//叶子结点个数
#define m 2*n-1 //树的个数

typedef struct HFTree {
	int weight;	//权值
	int parent;	//双亲
	int left;	//左儿子
	int right;	//右儿子
}*HF;
HF hftree[m+1];	//下标从1开始计数,树的结点都是存在这个数组里的,通过下标来访问

//2、创建哈夫曼树

先将n个带权值的结点看作n个树(只有一个结点的树),再将两个权值最小的树合并在一起形成一棵新树,新树的根节点权值就为左右子树的权值之和,然后让新树继续和没有合并过的树进行合并,直到合并完只剩一棵树,这棵树就是哈夫曼树。

//创建哈夫曼树
void createHftree() {
	//先初始化数组里的所有结点,因为最后合并了树必然会有m个结点
	for (int i = 1; i <= m; i++) {
		hftree[i] = (HF)malloc(sizeof(struct HFTree));
		hftree[i]->weight = 0;
		hftree[i]->left = 0;
		hftree[i]->parent = 0;
		hftree[i]->right = 0;
	}
	//给每个结点赋值权值
    //这里只赋值到下标为n的结点,因为最开始只有n个结点
	for (int i = 1; i <= n; i++) {
		scanf("%d", &hftree[i]->weight);
	}
	//合并树
	for (int i = n + 1; i <= m; i++) {
		//定义两个变量作两个最小权值的结点的下标
		int p1 = 0;
		int p2 = 0;
		//定义两个变量作两个最小的权值
		int s1 = 32767;//32767是16位计算机下最大的int值
		int s2 = 32767;
		//找出最小的两个值
		for (int j = 1; j <= i-1; j++) {
			if (hftree[j]->parent == 0) {//没有双亲结点就表示他还没有被合并
				if (hftree[j]->weight < s1) {
					s2 = s1;
					s1 = hftree[j]->weight;
					p2 = p1;
					p1 = j;
				}
				else if(hftree[j]->weight<s2){
					s2 = hftree[j]->weight;
					p2 = j;
				}
			}
   		}
		//合并操作
		hftree[p1]->parent = i;
		hftree[p2]->parent = i;
		hftree[i]->left = p1;
		hftree[i]->right = p2;
		hftree[i]->weight = hftree[p1]->weight + hftree[p2]->weight;
	}
}

//3、 根到叶子的路径

1、路径和路径长度:一棵树中,任意两个结点之间的通路称为路径;通路中分支的数量称为路径长度。

2、权及带权路径长度:权是人为假定的赋值给结点的一个值;从根结点到该结点的? 路径长度*权值? 就是带权路径长度。

3、树的带权路径长度:所有叶子结点的带权路径长度之和为树的带权路径长度。

//根到叶子的路径
//用一个辅助栈
typedef struct PathNode {
	HF stack[m];	//辅助栈
	int top;	//栈顶指针
	int len;	//每一条路径的路径长度
}*PathNode;
PathNode s;
int num = 0;//作树的带权路径
//初始化栈
void init() {
	s->top = -1;
	s->len = -1;
}
//压栈
void Push(HF root) {
	s->stack[++s->top] = root;
	s->len++;
}
//出栈
void Pop() {
	s->top--;
	s->len--;
}
//打印栈
void PrintStack() {
	printf("%d", s->stack[0]->weight);
	int i = 1;
	for (; i <= s->top; ++i) {
		printf("-->%d", s->stack[i]->weight);
	}
	printf(",路径长度为:%d,", s->len);
	printf("带权路径长度为:%d\n", (s->len)*(s->stack[--i]->weight));
	num += (s->len) * (s->stack[i]->weight);//累加树的带权路径
}
//树的路径
void Path(HF root) {
	if (root == NULL)
		return;
	//先把结点压栈
	Push(root);
	//如果是叶子结点就打印栈里的结点
	if (root->left == 0 && root->right == 0)
		PrintStack(); 
	//如果不是,就递归子树
	else {
		Path(hftree[root->left]);
		Path(hftree[root->right]);
	}
	//出栈,改变路径的终点
	Pop();
}

?//4、哈夫曼编码

规定:往左编码为 0 ,往右编码为 1 。

在得到一个结点的编码时,用一个栈暂存编码,结点有左儿子就会递归它的左儿子,所以在此前会往栈里存一个0,如果左子树递归完没有找到目标结点并且栈里有元素的话,那就要退栈,然后回到原来结点递归右儿子,继续上述操作,直到找到了目标结点。

//哈夫曼编码
//用一个辅助栈,用栈来存储编码,向左走就存0,向右就存1
typedef struct Stack {
	int stack[n];	//辅助栈
	int top;	//栈顶指针
}*Stack;
Stack S;
int ok = 0;	//判定变量,为0表示没有找到目标结点
//初始化栈
void init1() {
	S = (Stack)malloc(sizeof(struct Stack));
	S->top = 0;
	ok = 0;	//每一次初始化就顺便给这个变量也初始化了,方便第二次用
}
//哈夫曼编码
void HfCode(HF root,int weight) {    //weight是要编码的结点的权值
	//当结点为空或者结点的权值是目标权值的时候返回
	if (root == NULL || root->weight == weight) {	
		if (root->weight == weight)//如果找到了目标权值的结点
			ok = 1;	//把ok变为1,代表已经找到了要编码的结点
		return;
	}
	//如果没有找到目标结点并且当前结点有左子树
	if (root->left&&ok==0) {
		S->stack[++S->top] = 0;	//因为是向左,所以存0
		HfCode(hftree[root->left], weight);	//然后递归左子树
		if (root->right&&ok==0) {	//如果没有找到目标结点并且当前结点有右子树
			S->stack[++S->top] = 1;	//向右,存1
			HfCode(hftree[root->right], weight);	//递归右子树
		}
	}
	//如果没有找到目标结点并且栈顶指针不为0,就要出栈
	if (ok == 0&&S->top>0)
		S->top--;
}
//打印编码
void PrintCode() {
	for (int i = 1; i <= S->top; i++) {
		printf("%d",  S->stack[i]);
	}
	printf("\n");
}

//完整测试源码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<malloc.h>

#define n 4	//叶子结点个数
#define m 2*n-1 //树的个数

typedef struct HFTree {
	int weight;	//权值
	int parent;	//双亲
	int left;	//左儿子
	int right;	//右儿子
}*HF;
HF hftree[m+1];	//下标从1开始计数
//创建哈夫曼树
void createHftree() {
	//初始化n个叶子结点
	for (int i = 1; i <= m; i++) {
		hftree[i] = (HF)malloc(sizeof(struct HFTree));
		hftree[i]->weight = 0;
		hftree[i]->left = 0;
		hftree[i]->parent = 0;
		hftree[i]->right = 0;
	}
	//给每个结点赋值权值
	for (int i = 1; i <= n; i++) {
		scanf("%d", &hftree[i]->weight);
	}
	//合并树
	for (int i = n + 1; i <= m; i++) {
		//定义两个变量作两个最小权值的结点的下标
		int p1 = 0;
		int p2 = 0;
		//定义两个变量作两个最小的权值
		int s1 = 32767;
		int s2 = 32767;
		//找出最小的两个值
		for (int j = 1; j <= i-1; j++) {
			if (hftree[j]->parent == 0) {
				if (hftree[j]->weight < s1) {
					s2 = s1;
					s1 = hftree[j]->weight;
					p2 = p1;
					p1 = j;
				}
				else if(hftree[j]->weight<s2){
					s2 = hftree[j]->weight;
					p2 = j;
				}
			}
   		}
		//合并操作
		hftree[p1]->parent = i;
		hftree[p2]->parent = i;
		hftree[i]->left = p1;
		hftree[i]->right = p2;
		hftree[i]->weight = hftree[p1]->weight + hftree[p2]->weight;
	}
}
//菜单
void menu() {
	printf("------------------\n");
	printf("----*哈夫曼树*----\n");
	printf("1、根到叶子的路径\n");
	printf("2、  哈夫曼编码  \n");
	printf("------------------\n");
}
//根到叶子的路径
//用一个辅助栈
typedef struct PathNode {
	HF stack[m];	//辅助栈
	int top;	//栈顶指针
	int len;	//每一条路径的路径长度
}*PathNode;
PathNode s;
int num = 0;//树的带权路径
//初始化栈
void init() {
	s->top = -1;
	s->len = -1;
}
//压栈
void Push(HF root) {
	s->stack[++s->top] = root;
	s->len++;
}
//出栈
void Pop() {
	s->top--;
	s->len--;
}
//打印栈
void PrintStack() {
	printf("%d", s->stack[0]->weight);
	int i = 1;
	for (; i <= s->top; ++i) {
		printf("-->%d", s->stack[i]->weight);
	}
	printf(",路径长度为:%d,", s->len);
	printf("带权路径长度为:%d\n", (s->len)*(s->stack[--i]->weight));
	num += (s->len) * (s->stack[i]->weight);//累加树的带权路径
}
//树的路径
void Path(HF root) {
	if (root == NULL)
		return;
	//先把结点压栈
	Push(root);
	//如果是叶子结点就打印栈里的结点
	if (root->left == 0 && root->right == 0)
		PrintStack(); 
	//如果不是,就递归子树
	else {
		Path(hftree[root->left]);
		Path(hftree[root->right]);
	}
	//出栈,改变路径的终点
	Pop();
}
//哈夫曼编码
//用一个辅助栈,用栈来存储编码,向左走就存0,向右就存1
typedef struct Stack {
	int stack[n];	//辅助栈
	int top;	//栈顶指针
}*Stack;
Stack S;
int ok = 0;	//判定变量
//初始化栈
void init1() {
	S = (Stack)malloc(sizeof(struct Stack));
	S->top = 0;
	ok = 0;	//每一次初始化就顺便给这个变量也初始化了,方便第二次用
}
//哈夫曼编码
void HfCode(HF root,int weight) {
	//当结点为空或者结点的权值是目标权值的时候返回
	if (root == NULL || root->weight == weight) {	
		if (root->weight == weight)//如果找到了目标权值的结点
			ok = 1;	//把ok变为1,代表已经找到了要编码的结点
		return;
	}
	//如果没有找到目标结点并且当前结点有左子树
	if (root->left&&ok==0) {
		S->stack[++S->top] = 0;	//因为是向左,所以存0
		HfCode(hftree[root->left], weight);	//然后递归左子树
		if (root->right&&ok==0) {	//如果没有找到目标结点并且当前结点有右子树
			S->stack[++S->top] = 1;	//向右,存1
			HfCode(hftree[root->right], weight);	//递归右子树
		}
	}
	//如果没有找到目标结点并且栈顶指针不为0,就要出栈
	if (ok == 0&&S->top>0)
		S->top--;
}
//打印编码
void PrintCode() {
	for (int i = 1; i <= S->top; i++) {
		printf("%d",  S->stack[i]);
	}
	printf("\n");
}
//主函数
int main() {
	int elem = 0;	//作要编码的结点的权值
	int chose = 0;
	printf("请输入%d个整数:\n", n);
	createHftree();	//创建哈夫曼
	while (1) {
		menu();
		scanf("%d", &chose);
		switch (chose) {
		case 1:
			s = (PathNode)malloc(sizeof(struct PathNode));
			init();	//初始化辅助栈
			Path(hftree[m]);	//路径
			printf("树的带权路径是:%d\n", num);
			num = 0;	//重置树的带权路径
			break;
		case 2:
			init1();	//初始化辅助栈
			printf("输入要编码的结点的权值:");
			scanf("%d", &elem);
			HfCode(hftree[m],elem);	//编码
			printf("权值为%d的结点的编码是:", elem);
			PrintCode();	//打印
			break;
		default:return;
		}
	}
	return 0;
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-11-11 12:57:43  更:2021-11-11 12:58:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:26:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码