哈夫曼树 C语言实现

哈夫曼树 C语言实现1 基本概念 a 路径和路径长度若在一棵树中存在着一个结点序列 k1 k2 kj 使得 ki 是 ki 1 的双亲 1 从 k1 到 kj 所经过的分支数称为这两点之间的路径长度 它等于路径上的结点数减 1 b 结点的权和带权路径长度在许多应用中 常常将树中的结点赋予一个有着某种意义的实数 我们称此实数为该结点的权 如下面一个树中的蓝色数字表示结点

1、基本概念

若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 kiki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径。

从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它等于路径上的结点数减1.

在许多应用中,常常将树中的结点赋予一个有着某种意义的实数,我们称此实数为该结点的权,(如下面一个树中的蓝色数字表示结点的权)

结点的带权路径长度规定为从树根结点到该结点之间的路径长度与该结点上权的乘积。

c、树的带权路径长度

树的带权路径长度定义为树中所有叶子结点的带权路径长度之和,公式为:

哈夫曼树 C语言实现

其中,n表示叶子结点的数目,wi 和 li 分别表示叶子结点 ki 的权值和树根结点到 ki 之间的路径长度。

d、哈夫曼树

哈夫曼树又称最优二叉树。它是 n 个带权叶子结点构成的所有二叉树中,带权路径长度 WPL 最小的二叉树。

如下图为一哈夫曼树示意图。

哈夫曼树 C语言实现

2、构造哈夫曼树

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:

 如:对 下图中的六个带权叶子结点来构造一棵哈夫曼树,步骤如下:

哈夫曼树 C语言实现



注意:为了使得到的哈夫曼树的结构尽量唯一,通常规定生成的哈夫曼树中每个结点的左子树根结点的权小于等于右子树根结点的权。

具体算法如下:

//2、根据数组 a 中 n 个权值建立一棵哈夫曼树,返回树根指针 struct BTreeNode* CreateHuffman(ElemType a[], int n) { int i, j; struct BTreeNode b, *q; b = malloc(n*sizeof(struct BTreeNode)); for (i = 0; i < n; i++) //初始化b指针数组,使每个指针元素指向a数组中对应的元素结点 { b[i] = malloc(sizeof(struct BTreeNode)); b[i]->data = a[i]; b[i]->left = b[i]->right = NULL; } for (i = 1; i < n; i++)//进行 n-1 次循环建立哈夫曼树 { //k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标 int k1 = -1, k2; for (j = 0; j < n; j++)//让k1初始指向森林中第一棵树,k2指向第二棵 { if (b[j] != NULL && k1 == -1) { k1 = j; continue; } if (b[j] != NULL) { k2 = j; break; } } for (j = k2; j < n; j++)//从当前森林中求出最小权值树和次最小 { if (b[j] != NULL) { if (b[j]->data < b[k1]->data) { k2 = k1; k1 = j; } else if (b[j]->data < b[k2]->data) k2 = j; } } //由最小权值树和次最小权值树建立一棵新树,q指向树根结点 q = malloc(sizeof(struct BTreeNode)); q->data = b[k1]->data + b[k2]->data; q->left = b[k1]; q->right = b[k2]; b[k1] = q;//将指向新树的指针赋给b指针数组中k1位置 b[k2] = NULL;//k2位置为空 } free(b); //删除动态建立的数组b return q; //返回整个哈夫曼树的树根指针 }

3、哈夫曼编码

在电报通信中,电文是以二进制的0、1序列传送的,每个字符对应一个二进制编码,为了缩短电文的总长度,采用不等长编码方式,构造哈夫曼树,

将每个字符的出现频率作为字符结点的权值赋予叶子结点,每个分支结点的左右分支分别用0和1编码,从树根结点到每个叶子结点的路径上

所经分支的0、1编码序列等于该叶子结点的二进制编码。如上文所示的哈夫曼编码如下:

哈夫曼树 C语言实现

a 的编码为:00

b 的编码为:01

c 的编码为:100

d 的编码为:1010

e 的编码为:1011

4、哈夫曼树的操作运算

以上文的哈夫曼树作为具体实例,用详细的程序展示哈夫曼树的操作运算

#include<stdio.h> #include<stdlib.h> typedef int ElemType; struct BTreeNode { ElemType data; struct BTreeNode* left; struct BTreeNode* right; }; //1、输出二叉树,可在前序遍历的基础上修改。采用广义表格式,元素类型为int void PrintBTree_int(struct BTreeNode* BT) { if (BT != NULL) { printf("%d", BT->data); //输出根结点的值 if (BT->left != NULL || BT->right != NULL) { printf("("); PrintBTree_int(BT->left); //输出左子树 if (BT->right != NULL) printf(","); PrintBTree_int(BT->right); //输出右子树 printf(")"); } } } //2、根据数组 a 中 n 个权值建立一棵哈夫曼树,返回树根指针 struct BTreeNode* CreateHuffman(ElemType a[], int n) { int i, j; struct BTreeNode b, *q; b = malloc(n*sizeof(struct BTreeNode)); for (i = 0; i < n; i++) //初始化b指针数组,使每个指针元素指向a数组中对应的元素结点 { b[i] = malloc(sizeof(struct BTreeNode)); b[i]->data = a[i]; b[i]->left = b[i]->right = NULL; } for (i = 1; i < n; i++)//进行 n-1 次循环建立哈夫曼树 { //k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标 int k1 = -1, k2; for (j = 0; j < n; j++)//让k1初始指向森林中第一棵树,k2指向第二棵 { if (b[j] != NULL && k1 == -1) { k1 = j; continue; } if (b[j] != NULL) { k2 = j; break; } } for (j = k2; j < n; j++)//从当前森林中求出最小权值树和次最小 { if (b[j] != NULL) { if (b[j]->data < b[k1]->data) { k2 = k1; k1 = j; } else if (b[j]->data < b[k2]->data) k2 = j; } } //由最小权值树和次最小权值树建立一棵新树,q指向树根结点 q = malloc(sizeof(struct BTreeNode)); q->data = b[k1]->data + b[k2]->data; q->left = b[k1]; q->right = b[k2]; b[k1] = q;//将指向新树的指针赋给b指针数组中k1位置 b[k2] = NULL;//k2位置为空 } free(b); //删除动态建立的数组b return q; //返回整个哈夫曼树的树根指针 } //3、求哈夫曼树的带权路径长度 ElemType WeightPathLength(struct BTreeNode* FBT, int len)//len初始为0 { if (FBT == NULL) //空树返回0 return 0; else { if (FBT->left == NULL && FBT->right == NULL)//访问到叶子结点 return FBT->data * len; else //访问到非叶子结点,进行递归调用,返回左右子树的带权路径长度之和,len递增 return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1); } } //4、哈夫曼编码(可以根据哈夫曼树带权路径长度的算法基础上进行修改) void HuffManCoding(struct BTreeNode* FBT, int len)//len初始值为0 { static int a[10];//定义静态数组a,保存每个叶子的编码,数组长度至少是树深度减一 if (FBT != NULL)//访问到叶子结点时输出其保存在数组a中的0和1序列编码 { if (FBT->left == NULL && FBT->right == NULL) { int i; printf("结点权值为%d的编码:", FBT->data); for (i = 0; i < len; i++) printf("%d", a[i]); printf("\n"); } else//访问到非叶子结点时分别向左右子树递归调用,并把分支上的0、1编码保存到数组a { //的对应元素中,向下深入一层时len值增1 a[len] = 0; HuffManCoding(FBT->left, len + 1); a[len] = 1; HuffManCoding(FBT->right, len + 1); } } } //主函数 void main() { int n, i; ElemType* a; struct BTreeNode* fbt; printf("从键盘输入待构造的哈夫曼树中带权叶子结点数n:"); while(1) { scanf("%d", &n); if (n > 1) break; else printf("重输n值:"); } a = malloc(n*sizeof(ElemType)); printf("从键盘输入%d个整数作为权值:", n); for (i = 0; i < n; i++) scanf(" %d", &a[i]); fbt = CreateHuffman(a, n); printf("广义表形式的哈夫曼树:"); PrintBTree_int(fbt); printf("\n"); printf("哈夫曼树的带权路径长度:"); printf("%d\n", WeightPathLength(fbt, 0)); printf("树中每个叶子结点的哈夫曼编码:\n"); HuffManCoding(fbt, 0); } 

运行结果:

哈夫曼树 C语言实现



版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/232194.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 2021.7goland激活码【2021.7最新】

    (2021.7goland激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html…

    2022年3月21日
    40
  • python标准库6张思维导图学明白「建议收藏」

    python标准库6张思维导图学明白「建议收藏」先呈上高清下载地址链接:https://pan.baidu.com/s/14x2Cno96vp67qPz0Ee4weA提取码:7j7g1、标准库概览标准库包含:数据库处理,输入输出存储…

    2022年7月20日
    17
  • J2EE架构简介_手机架构

    J2EE架构简介_手机架构J2EE体系结构简介J2EE(Java2Platform,EnterpriseEdition)即Java2平台企业版,它提供了基于组件的方式来设计、开发、组装和部署企业应用。J2EE使用多层分布式的应用模型,这个多层通常通过三层或四层来实现:①客户层,运行在客户计算机上的组件。②Web层,运行在J2EE服务器上的组件。③业务层,同样是运行在J2EE服务器上的组件。

    2022年10月11日
    4
  • Java中级面试题及答案(120道Java中级面试题大汇总)[通俗易懂]

    Java中级面试题及答案(120道Java中级面试题大汇总)[通俗易懂]Java中级面试题及答案【最新版及答案,干货!!!这是一个中级Java面试系列题中的第一部分。这一部分论述了可变参数,断言,垃圾回收,初始化器,令牌化,日期,日历等等Java核心问题。Java中级面试永远是程序员迈向成功的第一个门槛,想要面试成功,各种面试题的洗礼是必不可少的,下面就来看看小编精心整理的一些java高级工程师面试题及答案吧。如果不背Java面试题的答案,肯定面试会挂!这套Java面试题大全,希望对大家有帮助哈~博主已将以下这些面试题整理成了一个Java面试手册,是PDF版的

    2022年6月15日
    44
  • javascript 字符串包含判断_js去除字符串前后的空格

    javascript 字符串包含判断_js去除字符串前后的空格本文教程操作环境:windows7系统、jquery3.2.1版本,DELLG3电脑。小编介绍过js中使用indexOf()方法判断字符串包含某个字符(https://www.py.cn/web/js/22856.html),是一个很好用的方法,但除了这个方法之外,JavaScript中还有四种方法可以实现判断字符串包含某个字符:1、使用字符串search()方法search()方法用于检…

    2022年10月7日
    6
  • android之如何在两个activity之间传递handler_利用broadcast广播机制「建议收藏」

    这算是如何在两个activity之间传递handler的解决方案二了,解决方案一见http://blog.csdn.net/jason0539/article/details/18055259再重复一遍我遇到的问题,就是在MainActivity里面打开AnotherActivity去执行一些操作,相应的改变MainActivity里的一些布局或者执行一些动作,最开始想到的就是把MainAct

    2022年3月11日
    54

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号