哈夫曼树编码:
1.权重获取:统计这段英文中数的频率以赋权,或直接读取文件中已经赋好的权重,或手动输入权重,在代码中手动输入权重(初始化)时会覆盖掉原本有权重的文件。
2.找两个最小的数:如果有三个或以上的数则取前两个,无所谓。
3.两数相加值替代两数成为新的权重:此时相加值的两个孩子为2步骤中找到的最小两数,左小右大
4.重复步骤2.3直到剩下一个权重root:此时二叉树已经建好了
5.编码:从字符(叶子结点)开始往上访问父节点(涉及到一开始建立的数据结构就需要有字符,父节点,左孩子,右孩子)再根据父节点判断左右孩子,左孩子赋值0,右孩子赋值1,直到访问到root结点,停下,将刚刚生成的字符串反过来即为该字符的编码
6.每一个字符对应一个编码,存在一个字符串数组中即可对应编码
译码:
1.从文件中读取所有的01字符串编码开始一个一个遍历
2.从根节点开始,遍历到0则访问左孩子,遍历到1则访问右孩子,每访问到一个结点都要判断这个结点中有没有字符的存在,如果有则输出该字符然后下一次遍历再从根节点开始,如果没有则再往下遍历。
3.输出所有的字符
代码:
//必做内容:
//一个完整的系统应具有以下功能:
//(1) I:初始化(Initialization)。建立霍夫曼树
// 从终端读入字符集大小n,以及n个字符和n个权值,建立霍夫曼树,并将它存于文件hfmTree中。
//(2) E:编码(Encoding)。
// 利用已建好的霍夫曼树(如不在内存,则从文件hfmTree中读入),
// 对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
//(3) D:译码(Decoding)。
//利用已建好的霍夫曼树将文件CodeFile中的代码进行译码,结果存入文件Textfile中。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
//哈夫曼树结点定义
struct huffmannode {
char ch;//字符区域
float weight;//字符对应权重区域
int lchild, rchild, parent;
};
//编码对应的结构体
struct chcode {
char c;
string codec;
};
//哈夫曼树的类定义
class huffmantree {
int num;//当前树一共有多少个叶子结点
huffmannode* HT = NULL;
char** HC;
//生成编码
void hfmcode();
public:
huffmantree(char c[], float w[], int n);//字符数组 对应权重数组 字符个数
//编码文件
void encoding(string file,string file1);
//译码文件
void decoding(string file,string file1);
void selectmin(int& s1, int& s2, int nn);
~huffmantree();
};
// 建立哈夫曼树
// 找到最小的两个结点生成一个新的结点
// 遍历权重数组,先找到一个最小值存入结点替换数据为-1//标记为已使用
// 再找到另一个结点替换数据为这两个数据之和 字符则设为*
// 两个结点的parent指向新结点 新结点的lchild指向权重小的 rchild指向权重大的
// 相同的则指向前面的
// 重复n-1次生成n-1个结点
// 最后一个结点指向root
//一共2n-1个结点 (用数组存?
huffmantree::huffmantree(char c[], float w[], int n) {
num = n;
//传入已经遍历完成的数组
HT = new huffmannode[2 * n];
for (int i = 1; i <= 2 * n - 1; i++) {
HT[i].lchild = 0;
HT[i].rchild = 0;
HT[i].parent = 0;
}
//初始化加入
for (int i = 1; i <= n; i++) {
HT[i].ch = c[i];
HT[i].weight = w[i];
//cout << HT[i].ch << "," << HT[i].weight << endl;
}
int s1, s2;
for (int i = n+1; i < 2 * n ; i++) {
//cout << i << endl;
selectmin(s1, s2, i-1);
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].weight = HT[s1].weight + HT[s2].weight;
//cout << HT[s1].weight << " " << HT[s2].weight << endl;
}
//生成编码写入hfmTree文件
hfmcode();
}
void huffmantree::selectmin(int& s1, int& s2, int nn) {
s1 = 0; s2 = 0;
HT[0].weight = 10000;
for (int i = 1; i <= nn; i++) {
if (HT[i].parent != 0) continue;
if (HT[i].weight < HT[s1].weight) {
s2 = s1;
s1 = i;
}
else if (HT[i].weight < HT[s2].weight ) s2 = i;
else continue;
}
//cout << s1 << " " << s2 << endl;
return;
}
void huffmantree::hfmcode() {
char* code = new char[num];
code[num-1] = '\0';
HC = new char* [num+1];
ofstream fout;
fout.open("hfmTree.txt", ios::out);
if (!fout.is_open()) {
cout << "file could not open!" << endl;
return;
}
for (int i = 1; i <= num; i++) {
int start = num-1; int c = i; int f = HT[i].parent;
while (f != 0) {
start--;
if (HT[f].lchild == c) code[start] = '0';
else code[start] = '1';
c = f;
f = HT[f].parent;
}
HC[i] = new char[num - start];
strcpy(HC[i] ,&code[start]);
fout << HT[i].ch << endl;
fout << HC[i] << endl;
}
delete []code;
//cout << "success!" << endl;
fout.close();
}
huffmantree::~huffmantree() {
delete[]HT;
}
//哈夫曼树的操作
// 编写单个字符的编码 在建立树后 生成每个字符的编码 存入字符串数组
// //左0右1
// 编写一个文件的编码 传入文件 对应字符进行编码写入文件
//hfmtree.encoding("ToBeTran.txt", "CodeFile");
void huffmantree::encoding(string file, string file1) {
ifstream infile("hfmTree.txt");
if (!infile.is_open()) {
cout << "hfmTree.txt could not open!" << endl;
return;
}
chcode* cd = new chcode[num];
int i = 0;
string line;
while (getline(infile, line)) {
cd[i].c = line[0];
getline(infile, line);
cd[i].codec = line;
i++;
}
//用结构体数组把之前的编码存起来
infile.close();
ifstream fin;
fin.open(file, ios::in);
if (!fin.is_open()) {
cout <<file<< " could not open!" << endl;
return;
}
ofstream fout;
fout.open(file1, ios::out);
if(!fout.is_open()) {
cout <<file1<< " could not open!" << endl;
return;
}
while (getline(fin, line)) {
for (int j = 0; j < line.length(); j++) {
for (int k = 0; k < num; k++) {
if (line[j] == cd[k].c) fout << cd[k].codec;
}
}
}
delete[]cd;
fout.close();
fin.close();
cout << "succeed encoding!" << endl;
}
// 译码 判断哈夫曼树是否为空 传入文件 进行译码:根据01遍历哈夫曼树输出相应字符
void huffmantree::decoding(string file, string file1) {
//ifstream infile("hfmTree.txt");
//if (!infile.is_open()) {
// cout << "hfmTree.txt could not open!" << endl;
// return;
//}
//chcode* cd = new chcode[num];
//int i = 0;
string line;
//while (getline(infile, line)) {
// cd[i].c = line[0];
// getline(infile, line);
// cd[i].codec = line;
// i++;
//}
用结构体数组把之前的编码存起来
//infile.close();
ifstream fin;
fin.open(file, ios::in);
if (!fin.is_open()) {
cout << file << " could not open!" << endl;
return;
}
ofstream fout;
fout.open(file1, ios::out);
if (!fout.is_open()) {
cout << file1 << " could not open!" << endl;
return;
}
int s = 2 * num - 1;
while (getline(fin, line)) {
for (int j = 0; j < line.length(); j++) {
if (line[j] == '0') s = HT[s].lchild;
else s = HT[s].rchild;
if(s<=num){
fout << HT[s].ch;
s = 2 * num - 1;
}
}
}
fout.close();
fin.close();
cout << "succeed decoding!" << endl;
}
//菜单
void meau() {
cout << "I------初始化" << endl
<< "E------编码" << endl
<< "D------译码" << endl
<< "Q------退出" << endl;
cout << "请输入功能:";
}
int main() {
char q;
while (true) {
meau();
cin >> q;
if (q == 'Q') return 0;
//遍历hfm文件 存入两个数组
// //第一行采用 fin>>a fin.get() 读一个换行符号
//第二行采用c=fin.get() 可以读空格符号 直到读到最后一个回车
//第二行循环a次读 fin>>数组[i]
ifstream fin;
fin.open("hfm.txt", ios::in);
if (!fin.is_open()) {
cout << "file could not open!" << endl;
break;
}
int a;
fin >> a;//字符个数
fin.get();
char* cha = new char[a+1];
float* we = new float[a+1];
for (int i = 1; i <= a; i++) {
cha[i] = fin.get();
fin.get();
}
fin.get();
for (int i = 1; i <= a; i++) {
fin >> we[i];
}
fin.close();
/*cout << a << endl;
for (int i = 0; i < a; i++) {
cout << cha[i] << "," << we[i] << endl;
}*/
//读hfm文件存入数组中 先根据hfm文件建初始的哈夫曼树
huffmantree hfmtree(cha, we, a);
if (q == 'I') {
//初始化(覆盖原来的hfm文件)文件写入操作
//如果初始化了就要覆盖之前写入的文件
//如果没有初始化就从原来的hfm文件中读入初始化
//最终建立哈夫曼树都是要用hfm文件
文件格式定义:一行字符加一行对应的权值 一行后回车也就是最后一个变量后面没有空格///读写空格符号?
先读一行计算字符的数量 要建立对应的字符数组和权值数组
//文件格式 第一行字符个数 第二行字符 第三行权值
ofstream fout;
fout.open("hfm.txt", ios::out);
cout << "请输入字符集大小:";
int a;
while (true) {
cin >> a;
if (a > 0) break;
cout << "字符集必须大于0,请重新输入:";
}
fout << a << endl;
cout << "请输入" << a << "个字符(以空格间隔):";
char A;
for (int i = 1; i <= a; i++) {
cin >> A;
fout << A << ' ';
}
fout << endl;
cout << "请输入" << a << "个字符对应的权值(以空格间隔):";
float wei;
for (int i = 1; i <= a; i++) {
cin >> wei;
fout << wei << ' ';
}
fout << endl;
fout.close();
}
else if (q == 'E') {
//编码(建立哈夫曼树) 文件读出操作 文件写入操作
//读文件tobetran 进行编码 结果存入文件codefile中
//输出一下字符串数组 编码文件 编码结果
hfmtree.encoding("ToBeTran.txt", "CodeFile.txt");
}
else if (q == 'D') {
//译码 文件读出操作 文件写入
//哈夫曼树建好的情况下才能继续进行
//读文件codefile 进行译码 结果存入文件textfile中
//输出一下译码结果
hfmtree.decoding("CodeFile.txt", "Textfile.txt");
}
else {
cout << "无法此功能,请重新输入" << endl;
}
system("pause");
system("cls");//清空屏幕
}
}