功能:
(1)初始化N个字符及其权值,建立哈夫曼树;
(2)编码:利用已经建立好的哈夫曼树对正文进行编码,将结果存入文件中;
(3)译码:利用已建立好的哈夫曼树将文件中的代码进行译码,结果存入另一个文件中;
(4)显示哈夫曼树;
(5)显示正文的编码及译码结果;
文章目录
一、实现
1、建立哈夫曼树
(1)初始化:首先动态申请m+1个单元;然后循环m次,将他们的双亲、左孩子、右孩子的下标都初始化为0。
m = 2*n-1;
HT = new HTNode[m+1]; //从下标1开始,所以需要分配 m+1 个单元
for(int i = 1;i <= m;i++){ //将双亲、左孩子、右孩子的下标初始化为0
HT[i].parent = 0;
HT[i].lChild = 0;
HT[i].rChild = 0;
}
(2)输入n个字符及其权值:循环n次,输入n个字符及其权值,如果输入格式错误,更改cin的表态标识符和清空缓存区的数据流,并且重新输入n个字符及其权值。
for(int i = 1;i <= n;i++) {
cout << "请输入第" << i << "个字符及权值:";
cin >> HT[i].letter;cin >> HT[i].weight;
while(!cin){
cin.clear(); //更改cin的状态标示符
cin.sync(); //清除缓存区的数据流
cout << "格式错误, 重新输入\n";
cin >> HT[i].letter;cin >> HT[i].weight;
}
}
(3)创建哈夫曼树:循环n-1次,通过n-1次的选择、删除、合并来创建哈夫曼树。选择:选择当前森林中选择双亲为0并且权值最小的两个树根结点s1和s2;删除:将结点s1和s2的双亲改为i;合并:将s1和s2的权值之和作为双亲结点的权值,同时记录新结点左孩子下标为s1,右孩子下标为s2。
for(int i = n+1;i <= m;i++){ //n-1次循环
Select(HT,i-1,s1,s2); //找出权值最小的两个
HT[s1].parent = i;
HT[s2].parent = i; //将双亲设为i
HT[i].lChild = s1;
HT[i].rChild = s2; //将其作为左右孩子
HT[i].weight = HT[s1].weight + HT[s2].weight;
//双亲的权值为左右孩子权值之和
}
2、寻找最小的两个元素
(1)寻找最小的两个元素:首先找到两个双亲结点为0的结点,然后比较s1和s2的值,让s1的值小于s2的值;
for(i;i < n && HT[i].parent != 0;++i); //找到第一个双亲为0的点
j = i;
for(i = i+1;i < n && HT[i].parent != 0;++i); //找到第二个双亲为0的点
if(HT[i].weight < HT[j].weight){ //使s1<s2
s1 = i;s2 = j;
}
else{
s1 = j;s2 = i;
}
(2)再进行循环,比较s1、s2和后面双亲为0的结点,修改s1或者s2的值。
while((++i) <= n){ //找权值最小的两个点
if(HT[i].parent == 0){
if(HT[i].weight < HT[s1].weight){
s2 = s1;s1 = i;
}else if(HT[i].weight < HT[s2].weight)
s2 = i;
}
}
3、初始字符编码
(1)分配储存n个字符编码的编码表空间HC,长度为n+1;分配临时储存每个字符编码的动态数组空间cd,cd[n-1]置为‘\0’。
HC = new char*[n+1]; //分配n个字符编码的编码表空间
char* cd = new char[n]; //分配临时存放每个字符编码的动态数组空间
cd[n-1] = '\0'; //编码结束符
(2)逐个求解n个字符的编码,循环n次。
1.设置start用于记录编码在cd中存放的位置,start初始时指向最后,即编码结束符位置n-1;
for(int i = 1;i <= n;i++){
start = n-1; //start开始指向最后,即编码结束符的位置
2.设置child用于记录从叶子结点向上回溯至根节点所经过的下标,child初始时为当前待编码字符的下标i,parent用于记录i的双亲结点的下标;
child = i;
parent = HT[i].parent; //parent指向节点child的双亲节点
3.从叶子结点向上回溯至根结点,求得字符i的编码,当parent没有达到根结点时,回溯一次start向前指一个位置,即–start;
while(parent != 0){
--start; //回溯一次start向前指一个位置
4.若结点child是parent的左孩子,则生成0,否则生成1,生成的代码保存在cd中,继续向上回溯,改变child和parent的值;
if(HT[parent].lChild == child)
cd[start] = '0'; //为左孩子时,生成0
else
cd[start] = '1'; //为右孩子时,生成1
child = parent;
parent = HT[parent].parent; //继续向上回溯
}
5.根据数组cd的字符串长度为第i个字符编码分配空间HC[i],然后将数组cd的编码复制到HC[i]中,最后释放临时空间cd。
HC[i] = new char[n-start]; //为第i个字符编码分配空间
strcpy(HC[i],&cd[start]); //将求得的编码从临时空间cd复制到HC的行列中
}
delete cd; //释放临时空间
4、对正文编码
(1)打开code_text.txt文件,文件操作;
ifstream file;
file.open("code_text.txt");
ofstream fout("code_result.txt");
(2)一次读入文件中的每个字符,字符与初始化的字符(循环n次)进行匹配,匹配到将其标识为1;
while(file.get(ch)){
for(int i = 1;i <= n;i++){
if(ch == HT[i].letter){
(3)如果flag等于1,输入对应的字符编码,否则原样输入。
fout << HC[i];
flag = 1; //标志flag为1,匹配到初始化的字符
break;
}
else
flag = 0;
}
if(!flag)
fout << ch; //输出到文件
}
5、译码
(1)打开code_text.txt文件,文件操作;
ifstream file;
file.open("decode_text.txt");
ofstream fout("decode_result.txt");
(2)从根节点(HT[m])开始,从文件中读入数据,如果ch等于1,访问右子树,如果ch等于0,访问左子树。如果左子树和右子树都为0,代表当前字段解码完毕。再使m等于根节点,重新进行。
int m = 2*n-1;; //根节点
while(file.get(ch)){
if(ch == '1')
m = HT[m].rChild;
else if(ch == '0')
m = HT[m].lChild;
if(HT[m].lChild == 0 && HT[m].rChild == 0){ //当前字段解码完毕
fout << HT[m].letter;
m = 2*n-1;
}
}
6、显示哈夫曼树
(1)从根结点(m = 2*n-1;)开始,当m!=0或者栈不为空时进行循环;
int m = 2*n-1; //根节点
while(m != 0 || StackEmpty(S) == 0){
(2)当m!=0时,即从根结点开始一直到叶子节点,循环将所有右子树全部入栈;
while(m != 0){
Push(S,m); //入栈
m = HT[m].rChild; //遍历右子树
}
(3)当栈不为空时,取出栈顶元素;
if(!StackEmpty(S)){
Pop(S,m); //出栈
(4)利用当前结点离根结点的距离进行循环控制输出的位置;
k = 0;temp = m; //计算离根节点距离,美化输出
while(temp){
temp = HT[temp].parent;
k++;
}
for(int i = 1;i < k;i++)
printf(" ");
(5)打印当前节点的权值,并将m置为m的左子树
cout << HT[m].weight << endl;
m = HT[m].lChild; //遍历左子树
}
}
二、效果
1、建立哈夫曼树
2、显示字符编码
3、译码及编码
4、显示哈夫曼树
5、显示译码编码结果
译码内容(code_text.txt):
Computers are changing our life.You can do a lot of things with a computer.Such as, you can use a computer to write articles,watch video CDs,play games and do office work.But the most important use of a computer is to join the Internet.We don’t needto leave home to borrow books from a library or to do shopping in a supermarket.Computers help us live a more convenient life.
编码内容(decode_text.txt):
011110100101010101001000