C语言实现哈夫曼编码(最小堆,二叉树)
// 文件中有通过QT实现的界面
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef struct HNode *Heap; /* 堆的类型定义 */
typedef struct SData myData;
typedef struct SData *HuffmanTree;
typedef struct Ans SAns;
struct Ans // 存储最终结果
{
char ch; // 表示字符
char *s; // 一个字符串, 表示结点的哈夫曼编码
};
struct SData // 哈夫曼树的结构
{
int freq; // 结点元素出现的频率
char ch; // 结点元素的字符
HuffmanTree Left, Right; // 此哈夫曼结点的左子树和右子树
};
int i = ; // 充当遍历哈夫曼树时对结果结构体数组ans赋值的下标
struct HNode // 堆的结构体
{
myData *Data; // 存储元素的数组
int Size; // 堆中当前元素个数
int Capacity; // 堆的最大容量
};
typedef Heap MinHeap; // 最小堆 MinHeap CreateHeap(int MaxSize); // 创建最小堆
int IsFull(MinHeap H); // 判断最小堆是否已满
int Insert(MinHeap H, myData X); // 往最小堆中插入元素
int IsEmpty(MinHeap H); // 判断最小堆是否为空
myData DeleteMin(MinHeap H); // 删除最小堆顶的元素, 即最小元素(自定义'小')
char* MatchingString(char *s1, char *s2); // 连接不定长的字符串s1和s2, 返回新字符串
char* TraversalHT(myData *d, char *s, SAns *SAnsP); // 递归遍历哈弗曼树, 直到收录所有叶节点的数据
void InsertSort(SAns *ans, int N); // 插入排序
char* binarySearch(SAns *ans, char ch, int end); // 迭代实现的二分查找 int main()
{
MinHeap heap = CreateHeap();
char ch;
int freq;
printf("先输入数字代表权值(大于0),再输入对应的字符,按Ctrl+Z结束\n");
while (scanf("%d %c", &freq, &ch) != EOF)
{
myData temp = { freq, ch, NULL, NULL };
Insert(heap, temp); // 将输入的数据放在结构体中, 插入到最小堆里
}
HuffmanTree T;
const int size = heap->Size; // 定义const常量size为初始时刻堆的大小
SAns *ans = (SAns*)malloc(sizeof(SAns)*size);
// 通过连续两次取最小堆的堆顶元素, 且先取出的存放在左子树, 后取出的存放在右子树
// 合成一颗二叉树, 再将其插入最小堆中
// 反复进行此操作size-1次, 最终堆顶的元素就是我们所求的哈弗曼树
// 这里说的堆顶并不是指heap->Data[0], 因为heap->Data[0]已用于放置哨兵
for (int j = ; j < size; j++)
{
T = (HuffmanTree)malloc(sizeof(myData));
T->Left = (HuffmanTree)malloc(sizeof(myData));
T->Right = (HuffmanTree)malloc(sizeof(myData));
*T->Left = (DeleteMin(heap));
*T->Right = (DeleteMin(heap));
T->freq = T->Left->freq + T->Right->freq;
Insert(heap, *T);
// printf("%d\n", heap->Data[1].freq); // 输出总频率, 也可以说是权重, 检验结果是否正确
}
TraversalHT(&heap->Data[], "", ans); // 遍历哈弗曼树, 将结果放在ans结构体数组中 // for (int k = 0; k < size; k++) // 检验
// {
// printf("%c %s\n", ans[k].ch, ans[k].s);
// }
// printf("**********************\n"); InsertSort(ans, size); // 对ans数组进行插入排序, 元素的大小取决于字符ch的大小 // for (int k = 0; k < size; k++) // 检验排序的结果
// {
// printf("%c %s\n", ans[k].ch, ans[k].s);
// } printf("输入需要翻译的字符串:");
char str[]; // 开一个足够大的字符数组, 用于存放需要翻译的字符串
while (scanf("%s", str) != EOF)
{
for (int w = ; w < strlen(str); w++)
{
// 遍历str, 通过二分查找, 找到与字符对应的哈夫曼编码
printf("%s", binarySearch(ans, str[w], size - ));
}
printf("\n");
printf("输入需要翻译的字符串:");
} return ;
} char* binarySearch(SAns *ans, char ch, int end)
{
// 经典的二分查找, 算法详细过程省略...
int mid;
int beg = ;
while (end >= beg)
{
mid = (beg + end) / ;
if (ans[mid].ch > ch)
beg = mid + ;
else if (ans[mid].ch < ch)
end = mid - ;
else
return ans[mid].s;
}
return NULL; // 消除warning
} void InsertSort(SAns *ans, int N)
{
// 经典的插入排序, 算法详细过程省略...
for (int p = ; p < N; p++)
{
SAns temp = ans[p];
int i;
for (i = p; i > && ((int)ans[i - ].ch < (int)temp.ch); i--)
ans[i] = ans[i - ];
ans[i] = temp;
}
} char* MatchingString(char *s1, char *s2)
{
// 由于本程序采用C语言编写, 没有内置string类, 且需要连接不定长的字符串s1和s2
// 故先申请一块动态内存, 用于存放结果t
// 先将不定长的s1复制到t中, 之后直接通过strcat()函数把s2接在t后面,返回t
char *t = (char*)malloc(strlen(s1) + strlen(s2) + );
if (t == NULL)
exit();
strcpy(t, s1);
strcat(t, s2);
return t;
} char* TraversalHT(myData *d, char *s, SAns *SAnsP)
{
// 接收参数 d, s, SAnsP,
// d代表哈弗曼树结构, s用于存放遍历到此节点时的哈夫曼编码,
// SAnsP用于存放通过遍历得到的叶节点的字符和哈夫曼编码所构成的结果
if (d->Left) // 若d存在左子树, 则继续遍历其左子树, 并且在字符串s后面拼接上字符串"0"
TraversalHT(d->Left, MatchingString(s, ""), SAnsP);
else
{
// 不存在左子树, 则必定不存在右子树, 此时只需保存结果, 接着就可以返回NULL结束这次递归
SAns temp = { d->ch, s };
SAnsP[i] = temp;
printf("编码: %c %s\n", d->ch, s);
i++; // 结果数组下标+1
return NULL;
}
if (d->Right) // 若d存在右子树, 则继续遍历其右子树, 并且在字符串s后面拼接上字符串"1"
TraversalHT(d->Right, MatchingString(s, ""), SAnsP);
return NULL; // 消除warning
} MinHeap CreateHeap(int MaxSize)
{
// 创建容量为MaxSize的空的最小堆
MinHeap H = (MinHeap)malloc(sizeof(struct HNode));
H->Data = (myData *)malloc((MaxSize + ) * sizeof(myData));
H->Size = ;
H->Capacity = MaxSize;
H->Data[].freq = -; // 定义"哨兵"为小于堆中所有可能元素的值, 这里可以是-1
//有了哨兵就不必在后续的遍历中的for循环判断条件中加入(...&&i>1),可有效提高效率
return H;
} int IsFull(MinHeap H)
{
return (H->Size == H->Capacity);
} int Insert(MinHeap H, myData X)
{
// 将元素X插入最小堆H,其中H->Data[0]已经定义为哨兵
int i;
if (IsFull(H))
{
printf("最小堆已满\n");
return ;
}
i = ++H->Size; // i指向插入后堆中的最后一个元素的位置
for (; H->Data[i / ].freq > X.freq; i /= )
H->Data[i] = H->Data[i / ]; // 上滤X,最终i就是X的下标
H->Data[i] = X; // 将X插入
return ;
} int IsEmpty(MinHeap H)
{
return (H->Size == );
} myData DeleteMin(MinHeap H)
{
// 从最小堆H中取出键值为最小的元素,并删除一个结点
int Parent, Child;
myData MinItem, X; if (IsEmpty(H))
{
printf("最小堆已为空\n");
X.freq = -;
return X; //-1表示删除元素失败
} MinItem = H->Data[]; // 取出根结点存放的最小值
// 用最小堆中最后一个元素从根结点开始向上过滤下层结点
X = H->Data[H->Size--]; // 同时减小当前堆的规模
for (Parent = ; Parent * <= H->Size; Parent = Child)
{
Child = Parent * ;
if ((Child != H->Size) && (H->Data[Child].freq > H->Data[Child + ].freq))
Child++; // Child指向左右子结点的较小者
if (X.freq <= H->Data[Child].freq)
break; // 找到了合适位置
else // 下滤X
H->Data[Parent] = H->Data[Child];
}
H->Data[Parent] = X; return MinItem;
}
C语言实现哈夫曼编码(最小堆,二叉树)的更多相关文章
- 【视频编解码·学习笔记】7. 熵编码算法:基础知识 & 哈夫曼编码
一.熵编码概念: 熵越大越混乱 信息学中的熵: 用于度量消息的平均信息量,和信息的不确定性 越是随机的.前后不相关的信息,其熵越高 信源编码定理: 说明了香农熵越信源符号概率之间的关系 信息的熵为信源 ...
- 基于哈夫曼编码的压缩解压程序(C 语言)
这个程序是研一上学期的课程大作业.当时,跨专业的我只有一点 C 语言和数据结构基础,为此,我查阅了不少资料,再加上自己的思考和分析,实现后不断调试.测试和完善,耗时一周左右,在 2012/11/19 ...
- C语言数据结构之哈夫曼树及哈夫曼编码的实现
代码清单如下: #pragma once #include<stdio.h> #include"stdlib.h" #include <string.h> ...
- 基于哈夫曼编码的文件压缩(c++版)
本博客由Rcchio原创 我了解到很多压缩文件的程序是基于哈夫曼编码来实现的,所以产生了自己用哈夫曼编码写一个压缩软件的想法,经过查阅资料和自己的思考,我用c++语言写出了该程序,并通过这篇文章来记录 ...
- 霍夫曼编码(Huffman Coding)
霍夫曼编码(Huffman Coding)是一种编码方法,霍夫曼编码是可变字长编码(VLC)的一种. 霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符 ...
- 哈夫曼编码(Huffman coding)的那些事,(编码技术介绍和程序实现)
前言 哈夫曼编码(Huffman coding)是一种可变长的前缀码.哈夫曼编码使用的算法是David A. Huffman还是在MIT的学生时提出的,并且在1952年发表了名为<A Metho ...
- [C++]哈夫曼树(最优满二叉树) / 哈夫曼编码(贪心算法)
一 哈夫曼树 1.1 基本概念 算法思想 贪心算法(以局部最优,谋求全局最优) 适用范围 1 [(约束)可行]:它必须满足问题的约束 2 [局部最优]它是当前步骤中所有可行选择中最佳的局部选择 3 [ ...
- 哈夫曼(huffman)树和哈夫曼编码
哈夫曼树 哈夫曼树也叫最优二叉树(哈夫曼树) 问题:什么是哈夫曼树? 例:将学生的百分制成绩转换为五分制成绩:≥90 分: A,80-89分: B,70-79分: C,60-69分: D,<60 ...
- 数据结构之C语言实现哈夫曼树
1.基本概念 a.路径和路径长度 若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径. 从 ...
随机推荐
- 解析Django路由层URLconf
目录: 一 Django中路由的作用 二 路由的分组 三 路由分发 四 反向解析 五 名称空间 六 Django2.0版的path 一.Django中路由的作用 URL配置(URLconf ...
- 使用git上传项目到github的最基础命令
一.前言 把github作为自己项目托管的地方,实在是一个明智的选择.就算你不为自己项目考虑,你也要为你团队项目开发而学呀!可能有些初学者(比如我)会觉得git命令好多啊,又是各种术语,觉得好难上手. ...
- 转 lightmap
小记一下用法与问题,时更 surface shader就不用操心了,自带lightmap计算 主要是vertex fragment shader部分 Unity5 bake light map有三种情 ...
- 【BZOJ】1831: [AHOI2008]逆序对
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1831 考虑$-1$的位置上填写的数字一定是不降的. 令${f[i][j]}$表示$DP$到 ...
- 串口.Qt532测试(同步)
环境:Win7x64.Qt5.3.2 MSVC OpenGL(x86).vs2010(x86) ZC:这里的例子是 同步的函数操作,貌似 如果子线程在等待 WaitCommEvent(...)或Rea ...
- 项目上有红色感叹号, 一般就是依赖包有问题, remove依赖,重新加载,maven的也行可认删除,自己也会得新加载
项目上的红色叹号, 要下面提示: "Problems" 里的errors , 看是什么错误, 一般是由于网络等原因, 依赖没有下载完整, 只有文件名字对了, 内容不全, ...
- leecode第四题(寻找两个有序数组的中位数)
题解: class Solution { public: double findMedianSortedArrays(vector<int>& nums1, vector<i ...
- 离线人脸识别C#类库分享 虹软2.0版本
目前只封装了人脸检测部分的类库,供大家交流学习,肯定有问题,希望大家在阅读使用的时候及时反馈,谢谢!使用虹软技术开发完成 戳这里下载SDKgithub:https://github.com/dayAn ...
- PCB板的三种敷铜方法解析
1 do not pour over all same net objects:仅仅对相同网络的焊盘进行连接,其他如覆铜.导线不连接. 2 pour over all same net objects ...
- zlib 2.1.8 编译遇到的问题以及解决方法
环境:win7 x64 + vs2013 1.用vs2013打开zlib-1.2.8\contrib\vstudio\vc11\zlibvc.sln进行编译 包含了下面的的多个项目: miniunz: ...