算法分析与设计实验报告——实现哈夫曼编码
目录:
一、 实验目的
掌握贪心算法的基本思想和解决问题的基本步骤,认识贪心算法和动态规划的联系与区别,对比解决同一问题的两种算法设计策略的时间复杂性。
二、实验要求
用c++语言实现用贪心算法解决哈夫曼编码问题,分析时间复杂性,体会贪心算法解决问题的基本思路和步骤。
三、 实验原理
1、哈夫曼树的定义:假设有n个权值,试构造一颗有n个叶子节点的二叉树,每个叶子带权值为wi,其中树带权路径最小的二叉树成为哈夫曼树或者最优二叉树;
2、哈夫曼树的构造:weight为输入的字符权值,parent为这个节点的父节点的位置,lchild为这个节点的左孩子的位置,rchild为这个节点的右孩子的位置,即每一个HTNode对应一个结点。然后根据weight的值,取出现存子树中的最小与次小的MinCode,并且这两个子树没有父节点,把他们俩相连接成为一颗新树。如此往复构造出哈夫曼树。通过已经构造出的哈夫曼树,编码时自底向上进行查找判断,直到parent为树的顶点为止。这样,根据每次向上搜索后,原节点为父节点的左孩子还是右孩子,来记录0或1,这样,每个字符都会有一个01编码与之唯一对应,并且任何编码没有前部分是同其他完整编码一样的;解码时从顶部向下查找,直到查找到叶子结点,输出对应的字符。
四、 实验过程(步骤)
见附件一
实验步骤、特点
重要源代码(流操作的部分要醒目的提示并注释)
五、 运行结果
见附件二
六、实验分析与讨论
一开始对哈夫曼树树不是很了解,构造的时候不知道如何去构造,查找资料后选择了一种以数组为主要操作对象的算法。还有这次写的程序比较长,有些变量应用错误,所以出现了很多奇怪的的bug。
七、实验特色与心得
这次试验写了很长的程序,直到我写完才发觉。这次实验让我对贪心算法与哈夫曼树有了更深的了解。而且这次程序比较长,所以我出现了应用变量错误的现象。这种错误也是我对问题的理解深度不够造成的。以后写程序要在深刻理解的基础上再动键盘。
附件一 实验过程(步骤)
#include <bits/stdc++.h>
#define maxn 1000
//Format width
#define fmw 10
using namespace std;
//哈夫曼树结点结构体
typedef struct {
int weight;
int parent;
int lchild;
int rchild;
} HTNode, *HuffmanTree;
//需要编码的字符串数组
static char N[maxn];
//定义哈夫曼编码数组(char型二级指针(指向指针的指针))
typedef char **HuffmanCode;
//最小与次小的结点
typedef struct {
int s1;
int s2;
} MinCode;
//函数声明
//报错函数
void Error(string message);
//哈夫曼编码函数
HuffmanCode HuffmanCoding(HuffmanTree &HT, HuffmanCode HC, int *w, int n);
//选出最小与次小的函数
MinCode Select(HuffmanTree HT, int n);
void Error(string message) {
cout << "Error:" << message << endl;
exit(1);
}
//哈夫曼树,哈夫曼编码,权值数组,叶子结点数量
HuffmanCode HuffmanCoding(HuffmanTree &HT, HuffmanCode HC, int *w, int n) {
int i, s1 = 0, s2 = 0;
//哈夫曼树
HuffmanTree p;
//暂存哈夫曼编码数组
char *cd;
//m为总结点数
int f, c, start, m;
//最小与次小节点
MinCode min;
//如果结点只有一个,不进行编码
if (n <= 1)
Error("Code too small");
//计算总结点数量
m = 2 * n - 1;
//动态创建哈夫曼树
HT = (HuffmanTree) malloc((m + 1) * sizeof(HTNode));
//HT前n个为叶子节点,后面n-1个为非叶子节点
//初始化n个叶子节点
for (p = HT, i = 0; i <= n; i++, p++, w++) {
p->weight = *w;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
//初始化从后面数n-1个非叶子节点
for (; i <= m; i++, p++) {
p->weight = 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
//创建哈夫曼树
for (i = n + 1; i <= m; i++) {
//寻找最小与次小
min = Select(HT, i - 1);
//最小
s1 = min.s1;
//次小
s2 = min.s2;
//合并树
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;//合并权值
}
//输出创建的树
cout << "哈夫曼树列表:" << endl;
cout << setw(fmw) << left << "序号" << setw(fmw) << left << "权值" << setw(fmw) << left << "父节点" << setw(fmw) << left
<< "左孩子" << setw(fmw) << left << "右孩子" << endl;
for (i = 1; i <= m; i++) {
cout << setw(fmw) << left << i << setw(fmw) << left << HT[i].weight << setw(fmw) << left << HT[i].parent
<< setw(fmw) << left << HT[i].lchild << setw(fmw) << left << HT[i].rchild << endl;
}
//动态创建哈夫曼编码
HC = (HuffmanCode) malloc((n + 1) * sizeof(char *));
//动态创建暂存哈夫曼编码数组
cd = (char *) malloc(n * sizeof(char *));
//最后一位赋值为结束符
cd[n - 1] = '\0';
//求解编码
for (i = 1; i <= n; i++) {
start = n - 1;
/*
* 左为0,右为1
*/
//从第一号结点往上回溯查找,逆序存放
for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent) {
//父节点的左子树与自己比较
if (HT[f].lchild == c)
cd[--start] = '0';
else
cd[--start] = '1';
}
//对第i个字符分配空间
HC[i] = (char *) malloc((n - start) * sizeof(char *));
//将cd暂存的复制到HC
strcpy(HC[i], &cd[start]);
}
free(cd);
return HC;
}
//寻找最小与次小的结点
MinCode Select(HuffmanTree HT, int n) {
//最小,次小
int min, secmin;
int i, s1, s2;
MinCode code;
s1 = 1;
s2 = 1;
//寻找最小值
min = 0x3f3f3f3f;
for (i = 1; i <= n; i++) {
if (HT[i].weight < min && HT[i].parent == 0) {
min = HT[i].weight;
s1 = i;
}
}
//寻找次小值
secmin = 0x3f3f3f3f;
for (i = 1; i <= n; i++) {
if ((HT[i].weight < secmin) && (i != s1) && HT[i].parent == 0) {
secmin = HT[i].weight;
s2 = i;
}
}
//封装返回
code.s1 = s1;
code.s2 = s2;
return code;
}
//解码 哈夫曼树,叶子结点数,解码字符
void HuffmanTranslateCoding(HuffmanTree HT, int n, char *ch) {
int m = 2 * n - 1;
int i, j = 0;
cout << "解码后:" << endl;
//循环到最后
while (ch[j] != '\0') {
i = m;
//从顶部向下查找
while (HT[i].lchild != 0 && HT[i].rchild != 0) {
if (ch[j] == '0')
//向左查找
i = HT[i].lchild;
else
//向右查找
i = HT[i].rchild;
j++;
}
//输出对应字符
cout << N[i - 1];
}
cout << endl;
}
//主函数
int main() {
HuffmanTree HT = NULL;
HuffmanCode HC = NULL;
int *w = NULL;
int i, n;
char tran[maxn];
cout << "请输入需要编码的字符串N:";
gets(N);
n = strlen(N);
w = (int *) malloc((n + 1) * sizeof(int *));
w[0] = 0;
cout << "请依次输入权值:" << endl;
for (i = 1; i <= n; i++) {
printf("w[%d]=", i);
cin >> w[i];
}
HC = HuffmanCoding(HT, HC, w, n);
//打印哈夫曼编码表
cout << "哈夫曼编码:" << endl;
cout << setw(fmw) << left << "字符" << setw(fmw) << left << "权值" << setw(fmw) << left << "编码" << endl;
for (i = 1; i <= n; i++) {
cout << setw(fmw) << left << N[i - 1] << setw(fmw) << left << w[i] << setw(fmw) << left << HC[i] << endl;
}
cout << "哈夫曼编码:";
for (i = 1; i <= n; i++) {
cout << HC[i];
}
cout << endl;
cout << "请输入需要解码的字符串T:";
getchar();
gets(tran);
HuffmanTranslateCoding(HT, n, tran);
return 0;
}
/*
abcdef
45
13
12
16
9
5
*/
/*
computer
19
21
2
3
6
7
10
32
*/