带权路径最小的二叉树称为最优二叉树或Huffman(哈夫曼树)。

Huffman树的构造

将节点的权值存入数组中,由数组开始构造Huffman树。初始化指针数组,指针指向含有权值的孤立节点。

b = malloc(n*sizeof(BTreeNode));
for (i = 0; i < n; i++) {
b[i] = malloc(sizeof(BTreeNode));
b[i]->data = a[i];
b[i]->left = NULL;
b[i]->right = NULL;
}

数组b中的指针可以理解为二叉树的根指针。

进行n - 1次循环建立Huffman树

选择b中根节点权值最小的两棵二叉树作为左右子树组成新的二叉树,新二叉树的根节点权值为两颗二叉树根节点权值的和。

将新二叉树添加到b中,并从b中删除原来的两棵二叉树。当b中只有一棵树时终止循环。

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(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位置为空

Huffman编码与解码

首先给出求带权路径的递归实现:

double WeightPathLength(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);
}
}

上述算法实际上通过双递归遍历了Huffman树。

改进上述算法得到求哈夫曼编码的实现:

static int index = 0;
char *c;
void HuffManCoding(FILE *fp, 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;
fprintf(fp,"%c %d:",c[index++],FBT->data);
for (i = 0; i < len; i++)
fprintf(fp,"%d", a[i]);
fprintf(fp,"\n");
}
else//访问到非叶子结点时分别向左右子树递归调用,并把分支上的0、1编码保存到数组a
{ //的对应元素中,向下深入一层时len值增1
a[len] = 0;
HuffManCoding(fp, FBT->left, len + 1);
a[len] = 1;
HuffManCoding(fp, FBT->right, len + 1);
}
}
}

节点的Huffman编码由它在Huffman树中的位置决定。从根节点到任意节点有且仅有一条路径,且路径可以唯一确定节点。因此规定从左子结点经过编码为0,从右子结点经过编码为1,路径序列作为编码。

由Huffman树和Huffman编码的性质可知,Huffman编码是一种不等长编码。在构造过程中,两个权值较小的节点生成一棵新的二叉树,根节点的权值为左右子节点的和,并不实际代表字符。也就是说,较短的编码不可能是较长编码的前缀。

Huffman树从叶子到根构造,靠近根的字符节点权值与几个靠近叶子的节点权值和相近,故而靠近根的字符节点权值较高即编码较短。

解码过程可以由字符串匹配来完成:

//Decoding
for(i = 0; code[i]; i++) {
for (j = 0; j < n; j++) {
t = 1;
for (k = 0; coding[j][k]; k++) {
if (code[i + k] != coding[j][k]) {
t = 0;
break;
}
}
if (t == 1) {
append(out,c[j]);
i = i + k - 1;
break;
}
}
}
printf("%s\n",out);

//Huffman.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h> typedef struct
{
int data;
struct BTreeNode* left;
struct BTreeNode* right;
}BTreeNode; #define M 32
char coding[M][M]; BTreeNode* CreateHuffman(int a[], int n)
{
int i, j;
BTreeNode **b, *q;
b = malloc(n*sizeof(BTreeNode));
for (i = 0; i < n; i++) {
b[i] = malloc(sizeof(BTreeNode));
b[i]->data = a[i];
b[i]->left = NULL;
b[i]->right = NULL;
}
for (i = 1; i < n; i++)//进行 n-1 次循环建立哈夫曼树
{
int k1 = -1, k2;
for (j = 0; j < n; j++) {
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 = malloc(sizeof(BTreeNode));
q->data = b[k1]->data + b[k2]->data;
q->left = b[k1];
q->right = b[k2]; b[k1] = q;
b[k2] = NULL;
}
free(b);
return q;
} double WeightPathLength(BTreeNode* FBT, int len)//len初始为0
{
if (FBT == NULL) {
return 0;
}
else {
if (FBT->left == NULL && FBT->right == NULL) {
return FBT->data * len;
}
else {
return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);
}
}
} static int index = 0;
char *c;
void HuffManCoding(FILE *fp, BTreeNode* FBT, int len)//len初始值为0
{
static int a[10];
if (FBT != NULL) {
if (FBT->left == NULL && FBT->right == NULL) {
int i;
fprintf(fp,"%c %d:",c[index++],FBT->data);
for (i = 0; i < len; i++)
fprintf(fp,"%d", a[i]);
fprintf(fp,"\n");
}
else {
a[len] = 0;
HuffManCoding(fp, FBT->left, len + 1);
a[len] = 1;
HuffManCoding(fp, FBT->right, len + 1);
}
}
} void append(char *str, char ch) {
int i;
for (i = 0; str[i];i++);
str[i] = ch;
str[i+1] = '\0';
} int main()
{
int i, j, k, n, t;
int* arr;
char ch, in[M] = {'\0'}, code[M*M] = {'\0'}, out[M] = {'\0'};
BTreeNode* fbt;
FILE *fp; //Input
freopen("test.in","r",stdin);
scanf("%d", &n);
arr = (int *)malloc(n * sizeof(int));
c = (char *)malloc(n * sizeof(char));
arr[0] = 186;
c[0] = ' ';
//原谅楼主这里偷懒,空格字符的输入有点麻烦所以直接写入了
for (i = 1; i < n; i++) {
getchar();
scanf("%c %d",&c[i],&arr[i]);
} //huffman coding
fbt = CreateHuffman(arr, n);
fp = fopen("code.txt","w");
HuffManCoding(fp, fbt, 0);
fflush(fp); //Encoding
fp = fopen("code.txt","r");
for (i = 0; i < n; i++) {
fgetc(fp);
fscanf(fp,"%c %d:%s", &t, &ch, &coding[i]);
}
fp = fopen("src.in","r");
fscanf(fp, "%s", in);
for (i = 0; in[i]; i++) {
for (j = 0; j < n; j++) {
if (c[j] == in[i]) {
strcat(code,coding[j]);
}
}
}
printf("%s\n",code); //Decoding
for(i = 0; code[i]; i++) {
for (j = 0; j < n; j++) {
t = 1;
for (k = 0; coding[j][k]; k++) {
if (code[i + k] != coding[j][k]) {
t = 0;
break;
}
}
if (t == 1) {
append(out,c[j]);
i = i + k - 1;
break;
}
}
}
printf("%s\n",out);
return 0;
}

测试数据:

test.in:

27
a 4
b 13
c 22
d 32
e 103
f 21
g 15
h 47
i 57
j 1
k 5
l 32
m 20
n 57
o 63
p 15
q 1
r 48
s 51
t 80
u 23
v 8
w 18
x 1
y 16
z 1

Huffman树与编码的更多相关文章

  1. Huffman树的编码译码

    上个学期做的课程设计,关于Huffman树的编码译码. 要求: 输入Huffman树各个叶结点的字符和权值,建立Huffman树并执行编码操作 输入一行仅由01组成的电文字符串,根据建立的Huffma ...

  2. Huffman树与编码的简单实现

    好久没写代码了,这个是一个朋友问的要C实现,由于不会C,就用JAVA写了个简单的.注释掉的代码属性按照原来朋友发的题里带的参数,发现没什么用就给注释掉了. package other; import ...

  3. Huffman树进行编码和译码

    //编码#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> ...

  4. Huffman树及其编码(STL array实现)

    这篇随笔主要是Huffman编码,构建哈夫曼树有各种各样的实现方法,如优先队列,数组构成的树等,但本质都是堆. 这里我用数组来存储数据,以堆的思想来构建一个哈弗曼树,并存入vector中,进而实现哈夫 ...

  5. Huffman树及其编解码

    Huffman树--编解码 介绍:   Huffman树可以根据输入的字符串中某个字符出现的次数来给某个字符设定一个权值,然后可以根据权值的大小给一个给定的字符串编码,或者对一串编码进行解码,可以用于 ...

  6. 构造数列Huffman树总耗费_蓝桥杯

    快排! /** 问题描述 Huffman树在编码中有着广泛的应用.在这里,我们只关心Huffman树的构造过程. 给出一列数{pi}={p0, p1, …, pn-1},用这列数构造Huffman树的 ...

  7. Java蓝桥杯练习题——Huffman树

    Huffman树在编码中有着广泛的应用.在这里,我们只关心Huffman树的构造过程. 给出一列数{pi}={p0, p1, -, pn-1},用这列数构造Huffman树的过程如下: 找到{pi}中 ...

  8. [数据结构与算法]哈夫曼(Huffman)树与哈夫曼编码

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  9. huffman树即Huffma编码的实现

    自己写的Huffman树生成与Huffman编码实现 (实现了核心功能 ,打出了每个字符的huffman编码 其他的懒得实现了,有兴趣的朋友可以自己在我的基础增加功能 ) /* 原创文章 转载请附上原 ...

随机推荐

  1. SVN代码管理发布

    1.svn的独立模式应用 2.svn钩子的应用(例如:代码提交前的文件格式限制,大小限制,代码发布svn成功后的备份等等) 3.大型企业的代码发布流程 有一些制度流程.逻辑方案 4.业务变更管理

  2. 团队作业第5周 - 测试与发布(Alpha版本)- 天冷记得穿秋裤队

    团队作业第5周 - 测试与发布(Alpha版本)- 天冷记得穿秋裤队 Alpha版本测试报告 在测试过程中总共发现了多少Bug?每个类别的Bug分别为多少个? 前后端至今一共发现有10个bug,修复的 ...

  3. Excel 多个单元格输入同样内容

    step1: 将这些单元格选定.方法:可以连续选,也可以 ctrl + select不连续选择: step2:输入你想输入的内容,PS:出现在最后选择的单元格中: step3:组合键:ctrl + e ...

  4. JS学习笔记3_函数表达式

    1.函数表达式与函数声明的区别 函数声明有“提升”(hoisting)的特性,而函数表达式没有.也就是说,函数声明会在加载代码时被预先加载到context中,而函数表达式只有在执行表达式语句时才会被加 ...

  5. asp.net core mvc 中间件之路由

    asp.net core mvc 中间件之路由 路由中间件 首先看路由中间件的源码 先用httpContext实例化一个路由上下文,然后把中间件接收到的路由添加到路由上下文的路由集合 然后把路由上下文 ...

  6. 【转】 js数组 Array 交集 并集 差集 去重

    原文:http://blog.csdn.net/ma_jiang/article/details/52672762 最劲项目需要用到js数组去重和交集的一些运算,我的数组元素个数可能到达1000以上, ...

  7. [leetcode.com]算法题目 - Restore IP Addresses

    Given a string containing only digits, restore it by returning all possible valid IP address combina ...

  8. 【文文殿下】WC2019游记

    Day0 今天早上三点半才睡着,五点起床,前往省城郑州.与省实验常老师汇合,坐上高铁,下午三点半多才到广州二中. 下午随便找了一个教室进去敲一敲代码,发现自己越来越菜了. 和一大堆网上的dalao面基 ...

  9. 【友情链接】各位dalao的博客

    同省神犇 HA队长 __stdcall HA chty_syq为文文讲过字符串 HA cdcq为文文讲过后缀数组① ② Bluesky007超强的 外省神犇 知名OIer黄学长 一个可爱的蓝孩子qwq ...

  10. <转>php中heredoc与nowdoc的使用方法

    http://www.361way.com/php-heredoc-nowdoc/3008.html 一.heredoc结构及用法 Heredoc 结构就象是没有使用双引号的双引号字符串,这就是说在 ...