1、基本概念

a、路径和路径长度

若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i

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

b、结点的权和带权路径长度

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

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

c、树的带权路径长度

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

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

如下图中树的带权路径长度 WPL = 9 x 2 + 12 x 2 + 15 x 2 + 6 x 3 + 3 x 4 + 5 x 4  =  122

d、哈夫曼树

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

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

2、构造哈夫曼树

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

(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);

(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;

(3)从森林中删除选取的两棵树,并将新树加入森林;

(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

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

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

具体算法如下:

//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编码序列等于该叶子结点的二进制编码。如上文所示的哈夫曼编码如下:

a 的编码为:00

b 的编码为:01

c 的编码为:100

d 的编码为:1010

e 的编码为:1011

f 的编码为:11

4、哈夫曼树的操作运算

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

#include

#include

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);

}

运行结果:

下面来看一道ACM题目

题目描述:

哈夫曼树,第一行输入一个数n,表示叶结点的个数。需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即weight,题目需要输出所有结点的值与权值的乘积之和。

输入:

输入有多组数据。

每组第一行输入一个数n,接着输入n个叶节点(叶节点权值不超过100,2<=n<=1000)。

输出:

输出权值。

样例输入:

5

1 2 2 5 9

样例输出:

37

ac代码

链表构建哈夫曼树(插入排序)

#include

#include

#define max 1001

struct htree

{

int weight;

struct htree *lchild;

struct htree *rchild;

struct htree *next;

};

void addNode(struct htree *,struct htree *);

struct htree* createHfmtree(int *,int);

int getWpl(struct htree *,int);

int main()

{

int w[max];

int i,n,wpl;

struct htree *ht;

while(scanf("%d",&n) != EOF)

{

for(i = 0; i < n; i ++)

{

scanf("%d",&w[i]);

}

ht = createHfmtree(w,n);

wpl = getWpl(ht,0);

printf("%d\n",wpl);

}

return 0;

}

struct htree* createHfmtree(int *w,int n)

{

int i;

struct htree *head,*pl,*pr,*proot;

head = (struct htree *)malloc(sizeof(struct htree));

head->next = NULL;

for(i = 0; i < n; i ++)

{

struct htree *pnode = malloc(sizeof(struct htree));

pnode->weight = *(w + i);

pnode->lchild = pnode->rchild = pnode->next = NULL;

addNode(head,pnode);

}

while(head->next)

{

if(head->next->next == NULL)

break;

pl = head->next;

pr = pl->next;

head->next = pr->next;

proot = (struct htree *)malloc(sizeof(struct htree));

proot->weight = pl->weight + pr->weight;

proot->lchild = pl;

proot->rchild = pr;

addNode(head,proot);

}

return head->next;

}

void addNode(struct htree *head,struct htree *pnode)

{

struct htree *t = head;

while(t->next && t->next->weight < pnode->weight)

t = t->next;

pnode->next = t->next;

t->next = pnode;

}

int getWpl(struct htree *ht,int level)

{

if(ht == NULL)

return 0;

if(!ht->lchild && !ht->rchild)

{

return ht->weight * level;

}

return getWpl(ht->lchild,level + 1) + getWpl(ht->rchild,level + 1);

}

总结

以上是编程之家为你收集整理的使用C语言详解霍夫曼树数据结构全部内容,希望文章能够帮你解决使用C语言详解霍夫曼树数据结构所遇到的程序开发问题。

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。

小编个人微信号 jb51ccc

喜欢与人分享编程技术与工作经验,欢迎加入编程之家官方交流群!

c语言霍夫曼函数,使用C语言详解霍夫曼树数据结构相关推荐

  1. C语言qsort()函数的使用(详解)

    目录 1.参数含义 1.首元素地址base 2.元素个数num 3.元素大小size 4.自定义比较函数compar 2.使用方式 1.头文件 2.compar的实现 3.整体代码 qsort()函数 ...

  2. python椭圆代码_Python实现霍夫圆和椭圆变换代码详解

    这篇文章主要介绍了Python实现霍夫圆和椭圆变换代码详解,具有一定借鉴价值,需要的朋友可以参考下 在极坐标中,圆的表示方式为: x=x0 rcosθ y=y0 rsinθ 圆心为(x0,y0),r为 ...

  3. C语言 memcpy 和 strcpy 函数区别 - C语言零基础入门教程

    目录 一.memcpy 函数/strcpy 函数简介 二.memcpy 函数/strcpy 函数实战 1.strcpy 函数属于字符串拷贝 2.memcpy 函数属于内存拷贝 三.猜你喜欢 零基础 C ...

  4. C语言 strcpy 和 strcpy_s 函数区别 - C语言零基础入门教程

    目录 一.strcpy_s 函数/strcpy 函数简介 1.strcpy 函数语法 2.strcpy_s 函数语法 二.strcpy/strcpy_s 函数实战 三.猜你喜欢 零基础 C/C++ 学 ...

  5. C语言的底层逻辑剖析函数篇(其二),0基础搞定函数,初识函数递归,超详解

    这里写目录标题 C语言的底层逻辑剖析函数篇(其二),0基础搞定函数,初识函数递归,超详解 开篇语 函数的调用(嵌套调用和链式访问) 1.嵌套调用 2.函数的链式访问 函数的声明和定义 函数声明和定义分 ...

  6. c语言 access编程,C语言中access/_access函数的使用实例详解

    在Linux下,access函数的声明在文件中,声明如下: int access(const char *pathname, int mode); access函数用来判断指定的文件或目录是否存在(F ...

  7. C语言(函数指针数组)详解

    要了解函数指针数组,可以从三个角度来分析.所谓函数指针数组,从字面意思上来解析,函数指针数组的组成有三个点,函数,指针,数组.首先我们知道,函数指针数组,是一个数组,数组的每个元素是函数指针,也就是一 ...

  8. time库是python中处理时间的标准库_python语言time库和datetime库基本使用详解

    今天是边复习边创作博客的第三天,我今年大二,我们专业开的有这门课程,因为喜欢所以更加认真学习,本以为没人看呢,看了后台浏览量让我更加认真创作,这篇博客花了2个半小时的时间,结合自己所学,所思,所想写作 ...

  9. C语言0长度数组(可变数组/柔性数组)详解

    CSDN GitHub C语言0长度数组(可变数组/柔性数组)详解 AderXCoding/language/c/zero_length_array 本作品采用知识共享署名-非商业性使用-相同方式共享 ...

最新文章

  1. c语言折半查找法程序,C语言基础:二分查找法演示代码
  2. java好用的hbase库_Hbase入库基于java
  3. 手机中geetest是什么文件_安卓手机系统中各类英文文件夹的含义详解,不知道的尽快熟知!...
  4. Bootloader的基本概念
  5. 11.1 问题描述及流程-机器学习笔记-斯坦福吴恩达教授
  6. 云计算--Presto
  7. sqlmap报错注入
  8. 前端学习(1434):为什么学习vue
  9. Nginx/Apache发大招
  10. Python 之 线程
  11. css 平行四边形 梯形 组合_微课|人教版五年级数学上册6.4组合图形的面积(P99)...
  12. es集群节点数和分片数关系_ES数据插入和查询流程是怎么样的?
  13. Bitmap 和Drawable 的区别
  14. 怎么终止linux的次序运行程序,linux – 如何按特定顺序停止systemd服务
  15. 运维自动化部署Cobbler之服务安装篇
  16. 前后端分离的思考与实践(四)
  17. Git 三款经典可视化管理工具-对比分享
  18. mysql sql注入工具下载_sql注入工具下载|超级SQL注入工具SSQLInjectionv1.0 正式版 附使用说明 - 极光下载站...
  19. mysql处理微信表情
  20. 用matlab2012制作机器人,利用matlab建立简单的机器人模型的步骤

热门文章

  1. 能源替代开辟取暖新时代 光伏赢得认可
  2. 解决织梦dedecms文档关键字(自动内链)php5.5以上失效的问题 urf-8版本的
  3. java非侵入式接口实现_No-intrusive, 非侵入式接口设计
  4. 携程手机版国际机票数据
  5. python输出古诗词_TensorFlow:基于RNN生成古诗词
  6. 微信端在电脑点不能点击下拉框
  7. Lync 地址簿同步知识点
  8. python画立体图形-Python绘制六种可视化图表详解,三维图最炫酷!你觉得呢?
  9. 入门图形学:光照模型(二)
  10. traceroute不通linux,PING能通,traceroute不通