树的企业应用-哈夫曼编码树-有趣的数据压缩算法
哈夫曼编码 描述
张三去李四家里,但 李四是一个女生,所以张三找李四去上海迪尼斯玩 …
亚历山大.张三去伊丽莎白.李四家里,但 伊丽莎白.李四是一个女生,所以亚历山大.张三找伊丽莎白.李四去美国迪尼斯玩 …
我们发现 一个关键点 有些重复的也不多 无非就高亮的地方 所以简化一下
声明一下 在上方的引言里 凡是出现高亮的 统统替换为 “张三” and “李四”
张三去李四家里,但 李四是一个女生,所以张三找李四去上海迪尼斯玩 ………
张三去李四家里,但 李四是一个女生,所以张三找李四去美国迪尼斯玩 ………
哈夫曼编码:把字符出现的次数压缩成只有一个
这就是哈夫曼编码 基本描述
哈夫曼编码树原理
字符串为 **"yyds ndd yygq bbq jjz xdxl jj qq wx dy wb bz ww " **
进行压缩变成如下表格:
字符 | 该字符出现的次数 |
---|---|
‘y’ | 5 |
‘d’ | 5 |
‘b’ | 4 |
‘g’ | 1 |
‘q’ | 4 |
‘z’ | 2 |
‘x’ | 3 |
‘j’ | 4 |
‘w’ | 4 |
’ ’ | 13 |
我们使用文本的方式储存这些 字符和出现的频率(注意:此博主比较懒输入,干脆整成一个文件)
input.txt:
y 5 d 5 g 1 q 4 z 2 x 3 j 4 w 4 13
那问题来了 哈夫曼编码树? 这些只是从文件读取到内存里整成了一个顺序表, 而已
我们使用C I/O的函数 来读取 文件 .
回过神来!
能看得出来这些是一体的 所以ndd(你懂的),我们应该定义 全新的数据类型 : Huffman_Code
哈夫曼编码树数据域定义
struct HuffmanCode{
char Val;//数据
int weight;//频率
}
定义完 先实现读取字符和频率
既然是顺序表 那还得用 统计 input.txt 文件的数据和频率多少个元素
const char* FileName = "input.txt";
const char* OpenMode = "r";
//读取的格式: 数据 空格 权值 空格
const char* readFormat = "%c %d ";
//统计文件中的数据和频率的个数并且通过参数修改
bool getSize(int& size) {
FILE* ReadStream = nullptr;
const errno_t openState = fopen_s(&ReadStream, FileName, OpenMode);
bool ret = openState == 0;
if (ret) {
int i = 0;
char ch = getc(ReadStream);
ret = ch != EOF;
if (ret) {
ungetc(ch, ReadStream);
HuffmanCodeValue value{
};
int ReadNum = 0;
while (!feof(ReadStream)) {
ReadNum = fscanf_s(ReadStream, readFormat, &value.value, sizeof(value.value), &value.weight, sizeof(value.weight));
++i;
}
size = i;
}
else {
cerr << "错误:" << FileName << "里文件为空" << endl;
}
fclose(ReadStream);
ReadStream = nullptr;
}
else {
char error[1024]{
};
strerror_s(error, errno);
cerr << "错误:" << error << endl;
}
return ret;
}
分配好内存 之后 读取文件中的数据
//获取文件 哈夫曼编码 以及编码的权值
void loadValue(HuffmanCodeValue* value, const int &size) {
if (size!=0){
FILE* ReadStream = nullptr;
const errno_t openState = fopen_s(&ReadStream, FileName, OpenMode);
bool ret = openState == 0;
if (ret) {
auto First = 0;
int ReadNum = 0;
char read[alignof(HuffmanCodeValue)+ 1];
while (First != size && !feof(ReadStream)) {
ReadNum = fscanf_s(ReadStream, readFormat, &value[First].value, sizeof(value[First].value), &value[First].weight, sizeof(value[First].weight));
++First;
}
fclose(ReadStream);
//free(ReadStream);
ReadStream = nullptr;
}
}
}
读取完毕后 ,是时候 该设计一些数据结构了(也是,最骚脑的时刻)
哈夫曼编码树的定义
//哈夫曼编码树节点
struct HuffmanCodeTreeNode{
HuffmanCode value;//数据
HuffmanCodeTreeNode* Parent;//父节点
HuffmanCodeTreeNode* LeftChild;//左子节点
HuffmanCodeTreeNode* RightChild;//右子节点
};
//哈夫曼编码树
struct HuffmanCodeTree {
HuffmanCodeTreeNode* root;//根节点
};
你以为到这里就结束了吗 …
那你就有点小瞧了哈夫曼编码树 还得按照优先级的频率来的
所以 还得吧写好的优先级队列搬出来
#ifndef __PriorityQueue_H__
#define __PriorityQueue_H__
#include"HuffmanTree.h"
using Element = HuffmanCodeTreeNode*;
using PriorityQueueNode = struct _PriorityQueueNode;
using PriorityCompare = bool (*)(const int &, const int &);
using PriorityQueueAuxiliary = struct _PriorityQueueAuxiliary;
using PriorityQueue = struct _PriorityQueue;
struct _PriorityQueueNode {
int Priority;
Element value;
PriorityQueueNode* next;
};
struct _PriorityQueueAuxiliary {
PriorityQueueNode* froot;
PriorityQueueNode* back;
};
struct _PriorityQueue {
PriorityCompare Compare;
size_t size;
PriorityQueueAuxiliary Auxiliary;
};
const size_t MaxSize = 1024;
void initPriorityQueue(PriorityQueue& priorityQueue, PriorityCompare c);
void PushPriorityQueue(PriorityQueue& priorityQueue,const Element& value);
bool fullPriorityQueue(const PriorityQueue& priorityQueue);
bool emptyPriorityQueue(const PriorityQueue& priorityQueue);
Element& PriorityQueueFroot(PriorityQueue& priorityQueue);// 禁止
//删除优先级队列 Element& value 设置到 value 里
void PopPriorityQueue(PriorityQueue& priorityQueue, Element& value);
void setCompare(PriorityQueue& priorityQueue, PriorityCompare Compare);
const size_t& PriorityQueueSize(PriorityQueue& priorityQueue);
void destroyPriorityQueue(PriorityQueue& priorityQueue);
#endif
PriorityQueue.cpp
#include"PriorityQueue.h"
#include<stdexcept>
using namespace std;
using Node = PriorityQueueNode;
const<