//哈夫曼编码
//https://www.bilibili.com/video/BV1wf421q7b8?vd_source=f8adf0d316b7398e145b8753f523e680&spm_id_from=333.788.player.switch
//https://www.bilibili.com/video/BV1qu411F7Zs?vd_source=f8adf0d316b7398e145b8753f523e680&spm_id_from=333.788.videopod.sections
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
struct HTNode {
//未存入字符则为'*'可以用其他不冲突的字符代替
char word = '*';//字符
double weight;//权重
int Pn;//父结点Parent-Node
int Ln, Rn;//左右子结点Left/Right-Node
};
typedef HTNode* HuffmanTree;
//找出森林集合中根权值最小的两个结点
void Select(HuffmanTree HT, int n, int& si, int& s2);
//构建哈夫曼树
void CreatHuffmanTree(HuffmanTree HT, int& n);
//先序遍历
void preOrder(HuffmanTree HT, int n);
//哈夫曼编码
void HuffmanCoding(HuffmanTree HT, int code[], int n);
//各字符的哈夫曼编码
void WordCoding(HuffmanTree HT, int node, int code[]);
int main()
{
cout << "请输入一串字符:" << endl;
//-------------------------------------
//该区域为文件操作
//读入文件至 字符串str
//未解决'\n'字符无法读入的问题
ifstream ifs;
ifs.open("HaffmanFile.txt");
string str;
if (ifs.is_open()) {
getline(ifs, str);
}
//不进行文件操作时由该被注释代码进行输入即可
//cin >> str;//待处理字符串
ifs.close();
cout << str << endl;
//------------------------------------
//该数组记录字符频率
HTNode array[256];
for (int i = 0; i < 256; i++) {
//初始化array数组
array[i].weight = 0;
array[i].Pn = 0;
array[i].Ln = array[i].Rn = -1;
}
array[1].word = str[0];
//记录存入字符的长度
//即array所含元素的长度
int length = 1;
bool ifAdd = true;
//字符数据存入-----
for (int i = 0; i < str.size(); i++) {
for (int j = 1; j <= length; j++) {
if (array[j].word == str[i]) {
array[j].weight++;
ifAdd = false;
}
}
if (ifAdd) {
length++;
array[length].word = str[i];
array[length].weight++;
}
else {
ifAdd = true;
}
}
//------------------------
cout << endl << length << endl;
//输出各字符元素的权---
for (int i = 1; i <= length; i++) {
array[i].weight /= str.size();
cout << array[i].word << " " << array[i].weight << endl;
}
//------------------------
cout << endl;
cout.setf(ios::showpoint);
CreatHuffmanTree(array, length);
//构成哈夫曼树后
//将树的各结点输出
for (int i = 1; i <= length; i++) {
cout << "[" << i << "]" << array[i].word << " " << array[i].weight << endl;
cout << "Pn[" << array[i].Pn << "]\t" << "Ln[" << array[i].Ln << "]\t" << "Rn[" << array[i].Rn << "]\n" << endl;
//分割字符结点与结合结点
if (array[i].word != '*' && array[i + 1].word == '*') {
cout << "\n------------------------------------------------------------------------\n";
}
}
preOrder(array, length);
cout << endl;
//将各结点的编码映射至该数组
//0-1
int code[256];
HuffmanCoding(array, code, length);
//测试code数组
//for (int i = 0; i <= length; i++) {
// cout << code[i];
//}
cout << endl;
//输出各字符的哈夫曼编码
for (int i = 1; i <= length; i++) {
//仅留下字符结点
if (array[i].word == '*') break;
cout << array[i].word << " ";
WordCoding(array, i, code);
cout << endl;
}
return 0;
}
//-------------------------------------------------------------
//
void Select(HuffmanTree HT, int n, int& s1, int& s2) {
int min = 1;
for (int i = 1; i <= n; i++) {
if (HT[i].Pn == 0) {
min = i;
break;
}
}
for (int i = 1; i <= n; i++) {
if (HT[i].Pn == 0 && HT[i].weight <= HT[min].weight && i != min) {
min = i;
}
}
s1 = min;
for (int i = 1; i <= n; i++) {
if (HT[i].Pn == 0 && i != s1) {
min = i;
break;
}
}
for (int i = 1; i < n; i++) {
if (i != s1) {
if (HT[i].Pn == 0 && HT[i].weight <= HT[min].weight && i != min) {
min = i;
}
}
}
s2 = min;
}
void CreatHuffmanTree(HuffmanTree HT, int& n) {
int s1, s2;
while (true) {
Select(HT, n, s1, s2);
if (s1 == s2) {
break;
}
//直接在array(HT)数组后
//添加结合结点
//以形成树结构
//
//n表示节点数量
//是在array(HT)数组所存入字符数量(length)上自增
//相当于记录了
//树的结点数量
++n;
HT[n].Ln = s1;
HT[s1].Pn = n;
HT[n].Rn = s2;
HT[s2].Pn = n;
HT[n].weight = HT[s1].weight + HT[s2].weight;
}
}
void preOrder(HuffmanTree HT, int n) {
if (n == 0 || n == -1)return;
if (HT[n].Ln == -1 || HT[n].Rn == -1) cout << HT[n].weight << " ";
preOrder(HT, HT[n].Ln);
preOrder(HT, HT[n].Rn);
}
//哈夫曼编码不唯一
//与根节点 子结点的 标记的区别(0/1)有关
void HuffmanCoding(HuffmanTree HT, int code[], int n) {
//n为树的结点数量
//也是根节点的位置
//node为结点编号
int node = n;
code[0] = 0;
code[n] = 0;
while (node != 0) {
if (HT[HT[node].Pn].Ln == node) {
code[node] = 0;
}
if (HT[HT[node].Pn].Rn == node) {
code[node] = 1;
}
--node;
}
}
void WordCoding(HuffmanTree HT, int node, int code[]) {
int next[256];
//num记录编码长度
//记录了最大下标
int num = 0;
while (true) {
next[num] = code[node];
node = HT[node].Pn;
if (node == 0) break;
++num;
}
//根节点到该结点的编码为哈夫曼编码
for (int i = num; i >= 0; i--) {
cout << next[i];
}
}
//2024-12-03;
哈夫曼编码学习记录C++
最新推荐文章于 2025-02-08 00:22:29 发布