从12点到晚上八点半,缝缝补补终于勉强写出来了,写完我自己都不想看这代码,乱七八糟,暂时的能力就在这了,虽然乱,还是有收获的
先把所有代码贴在这
#include<iostream>
#include<vector>
#include<fstream>
#include<string>
using namespace std;
#define SIZE 126//数组固定大小
#define HTNodes 251//根节点的父节点标号,以及叶节点的左右子树标号
struct HTNode{
int weight;//权重
vector<HTNode>::size_type parent;//父节点位置
vector<HTNode>::size_type lchild;//左子树位置
vector<HTNode>::size_type rchild;//右子树位置
vector<int> codes;
};//haffmanTree的节点
void Select(vector<HTNode> HaffmanTree, vector<HTNode>::size_type &s1, vector<HTNode>::size_type &s2) {
//参数是HaffmanTree,以及要找的两个位置,s1为权重最小的,s2次之
s1 = s2 = HTNodes;
vector<HTNode>::size_type i = 0;
for ( ; s1==HTNodes; ++i) {
if (HaffmanTree[i].weight != 0 && HaffmanTree[i].parent == 0)
s1 = i;
}
for (; s2 == HTNodes; ++i) {
if (HaffmanTree[i].weight != 0 && HaffmanTree[i].parent == 0)
s2= i;
}
for (; i != HaffmanTree.size(); ++i) {
if (HaffmanTree[i].weight != 0 && HaffmanTree[i].parent == 0) {
if (HaffmanTree[i].weight < HaffmanTree[s1].weight) {
s1 = i;
continue;
}
if (HaffmanTree[i].weight < HaffmanTree[s2].weight) {
s2 = i;
continue;
}
}
}
}
vector<HTNode> CreatHaffmanTree(vector<int> w) {//根据权重创建haffmanTree
vector<HTNode> HaffmanTree(SIZE, { 0,0,HTNodes,HTNodes });//创建HaffmanTree并初始化,共SIZE个叶节点
vector<HTNode>::size_type HTpos= 0;//HaffmanTree中的当前位置
vector<int>::size_type wpos = 0; //w的当前位置
int valid = 0;//w中权重不为0的个数,后续向数中添加节点用
for (; wpos != w.size(); ++HTpos,++wpos) {//将HaffmanTree的叶节点权值加上,即前n个元素权值加上
HaffmanTree[HTpos].weight = w[wpos];
if (w[wpos] != 0)
++valid;
}
if (valid < 1) {
cout << "请正确输入";
return HaffmanTree;
}
if (valid == 1) {
for (vector<HTNode>::size_type j = 0; j != HaffmanTree.size(); ++j) {
if (HaffmanTree[j].weight != 0) {
HaffmanTree[j].parent = HTNodes;
}
}
cout << "单个字符无需编码" << endl;
return HaffmanTree;
}
for (int i=valid;i!=1 ;--i) {//确立HaffmanTree,保证最后一个节点为根节点
vector<HTNode>::size_type s1 = 0, s2 = s1;
Select(HaffmanTree, s1, s2);
HTNode tem = { HaffmanTree[s1].weight + HaffmanTree[s2].weight,0,s1,s2 };
HaffmanTree.push_back(tem);
HaffmanTree[s1].parent = HaffmanTree.size();
HaffmanTree[s2].parent = HaffmanTree.size();
}
HaffmanTree[HaffmanTree.size() - 1].parent = HTNodes;//将根节点的父节点位置设为预定量
return HaffmanTree;
}
void HaffmanCoding(vector<HTNode>& HaffmanTree,vector<HTNode>::size_type pos) {
if (HaffmanTree[pos].lchild != HTNodes) {
HaffmanTree[HaffmanTree[pos].lchild].codes = HaffmanTree[pos].codes;
HaffmanTree[HaffmanTree[pos].lchild].codes.push_back(0);
HaffmanCoding(HaffmanTree, HaffmanTree[pos].lchild);
}
if (HaffmanTree[pos].rchild != HTNodes) {
HaffmanTree[HaffmanTree[pos].rchild].codes = HaffmanTree[pos].codes;
HaffmanTree[HaffmanTree[pos].rchild].codes.push_back(1);
HaffmanCoding(HaffmanTree, HaffmanTree[pos].rchild);
}
}
//打印编码
void PrintCodes(vector<HTNode>& HaffmanTree) {
for (vector<HTNode>::size_type i = 0; i != SIZE; ++i) {
if (HaffmanTree[i].weight != 0) {
cout << static_cast<char>(i) << ' ';
for (auto j : HaffmanTree[i].codes)
cout << j;
cout << endl;
}
}
}
void CodeFile(vector<HTNode>& HaffmanTree) {
vector<int> w(SIZE);
string SourceFile, TargetFile;
cout << "请输入原文件名:";
cin >> SourceFile;
cout << "请输入存储文件名:";
cin >> TargetFile;
ifstream infile(SourceFile);
if (!infile) {
cout << "原文件不存在";
return;
}
char a;
while (infile.get(a)) {
w[static_cast<int>(a)] += 1;
}
infile.clear();
infile.seekg(0,ios::beg);
ofstream outfile(TargetFile);
if(!outfile) {
cout << "存储文件不存在";
return;
}
HaffmanTree = CreatHaffmanTree(w);
HaffmanCoding(HaffmanTree, HaffmanTree.size() - 1);
cout << endl;
while (infile.get(a)) {
for (int i : HaffmanTree[static_cast<vector<HTNode>::size_type>(a)].codes)
outfile << i;
}
infile.close();
outfile.close();
}
void CodeString(vector<HTNode>& HaffmanTree) { //输入字符串并打印相应编码
vector<int> w(SIZE);
char a;
vector<char> s;
cout << "请输入字符,以#结束:";
while (cin.get(a)) {
if (a == '#')
break;
s.push_back(a);
w[static_cast<int>(a)] += 1;
}
HaffmanTree= CreatHaffmanTree(w);
HaffmanCoding(HaffmanTree, HaffmanTree.size() - 1);
PrintCodes(HaffmanTree);
for (auto c : s)
for (int i : HaffmanTree[static_cast<vector<HTNode>::size_type>(c)].codes)
cout << i;
}
void UnCode(vector<HTNode>& HaffmanTree) {
string SourceFile, TargetFile;
cout << "请输入原文件名:";
cin >> SourceFile;
cout << "请输入存储文件名:";
cin >> TargetFile;
ifstream infile(SourceFile);
if (!infile) {
cout << "原文件不存在";
return;
}
ofstream outfile(TargetFile);
if (!outfile) {
cout << "存储文件不存在";
return;
}
char a;
vector<int> source;
while (infile.get(a)) {
source.push_back(static_cast<int>(a)-48);
for (vector<HTNode>::size_type p=0; p != SIZE; ++p) {
if (source == HaffmanTree[p].codes) {
cout << static_cast<char>(p);
outfile << static_cast<char>(p);
source.clear();
break;
}
}
}
}
int main() {
vector<HTNode>HaffmanTree;
CodeFile(HaffmanTree);
//CodeString(HaffmanTree);
PrintCodes(HaffmanTree);
UnCode(HaffmanTree);
}
从注释情况也可以反映这心路历程,一开始写的注释都挺详细的,后来的就基本没有了。
关于解题思路:
首先是结构,树的每个节点都包含权值,父节点左右子树位置以及最后的编码
统计字符权值的数组我用的固定大小数组,对应ASCII码的前126位,原因主要是还不太会new和delete,如果用容器存储,那么统计权重需要每个字符都遍历一次容器,这样时间复杂度应该是O(n^2),而使用数组虽然空间更大一点,前126个字符可能会有权重为零的,但是统计查找都很简单,为O(n),所以我选择了固定数组。
构建HaffmanTree思路:
给前126个节点赋初值时统计权值不为零的字符个数为valid个,则后续应添加valid-1个节点每次添加时都在已有的节点中找到权值最小的两个节点作为新添加的节点的左右子树。依次进行直到结束
然后是根据Tree获得编码:
本次采用了从根节点向叶节点正向寻求编码的方式,方法类似于二叉树的遍历,运用递归,值得一提的是我在这不完美的规划下想到的方法是,每次到递归子树都用父节点的编码容器给子树的赋值以实现编码的逐层添加。
关于文件的操作主要是容器函数,IO操作,还不太熟悉,本次编码也算是令自己熟悉了学的某些知识。
言不及义,仅用来记录这写了一天bug的时光。