哈夫曼树 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)
上一篇 2025年10月26日 下午6:01
下一篇 2025年10月26日 下午6:22


相关推荐

  • Linux sleep命令[通俗易懂]

    Linux sleep命令[通俗易懂]Linuxsleep命令可以用来将目前动作延迟一段时间。使用权限:所有使用者。语法参数说明:–help:显示辅助讯息–version:显示版本编号number:时间长度,

    2022年7月1日
    24
  • 微信扫码登陆(1)—扫码登录流程讲解、获取授权登陆二维码

    微信扫码登陆(1)—扫码登录流程讲解、获取授权登陆二维码扫码登录流程讲解、获取授权登陆二维码具体流程可以看微信官网的扫码登录文档地址:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN其实官方文档已…

    2022年4月26日
    105
  • Cinemachine学习笔记

    Cinemachine学习笔记以下都是转载内容,能够比较直观的学习一些基础内容。现在的Cinemachine更新了许多新的功能,但是Cinemachine插件都ExamplesScences,去看一下官方例子和文档来学习更佳*版本要求Unity2017.1及以上。参考资料: [官方]Unity2017.1正式版发布 Cinemachine插件:Cinemachine。 结合Timeline实现动画:Unity…

    2022年5月28日
    48
  • HTML5知识初级题目

    在HTML5中,onblur和onfocus是:在HTML5中,哪个元素用于组合标题元素?HTML5中不再支持下面哪个元素?HTML5中不再支持下面哪个元素?HTML5之前的HT

    2021年12月21日
    50
  • Qt —— QWebEngineView加载谷歌离线地图(包含离线地图瓦片下载制作)

    Qt —— QWebEngineView加载谷歌离线地图(包含离线地图瓦片下载制作) 关注微信公众号搜索”Qt_io_”或”Qt开发者中心”了解更多关于Qt、C++开发知识.。笔者-jxd

    2026年2月2日
    12
  • 怎样使用cookie登录自己的账号

    在这之前,不管是做测试还是挖漏洞总会遇到这种问题做测试的时候测试项里面有一个会话标识未更新,这种漏洞说白了就是在退出个人账户的时候没有及时的清除cookie,从而让别人利用你的cookie再次登录你的账户,然后测试的时候客户就让测试如何使用cookie登录在挖漏洞的时候一般xss都需要打cookie然后伪造别人的身份登录,其实也是使用打到的cookie登录在这之前我没深入的理解这块,现…

    2022年4月8日
    82

发表回复

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

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