{
ch=Input[i]; //获得第i个字符
do
ﻩ{ﻩ
ﻩﻩpfront=pfront->next;
if((int)pfront->character==(int)ch)//如果在链表中有与当前字符相同得字符,该字符权值加1
ﻩ{
ﻩpfront->count++;
ﻩﻩﻩreplace=1;
ﻩﻩbreak;
ﻩﻩﻩ}
ﻩ}while(pfront->next);
if(!
replace) //如果在链表中没找到与当前字符相同得字符,则将该字符作为新成
员插入链表
{
ﻩﻩﻩNode*New=newNode;
ﻩﻩﻩif(!
New)
ﻩﻩthrowexception("堆空间用尽");
ﻩNew->character=ch;
ﻩﻩNew->count=1;
ﻩﻩNew->next=pfront->next;
ﻩﻩﻩpfront->next=New;
ﻩﻩn++;
ﻩ}
ﻩpfront=front; //重置pfront与replace变量为默认值
ﻩreplace=0;
ﻩ}
tSize=n; //tSize记录得就是编码表中字符个数
ﻩCreateHTree(front,n); //创建哈夫曼树
ﻩpfront=front;
while(pfront) //销毁整个链表
{
ﻩﻩfront=pfront;
ﻩpfront=pfront->next;
ﻩdeletefront;
}
时间复杂度:
若输入得字符串长度为n,则时间复杂度为O(n)
2、创建哈夫曼树(voidHuffmanTree:
:
CreateCodeTable(Node *p))
算法伪代码:
1.创建一个长度为2*tSize-1得三叉链表
2.将存储字符及其权值得链表中得字符逐个写入三叉链表得前tSize个结点得data域,并将对应结点得孩子域与双亲域赋为空
3.从三叉链表得第tSize个结点开始,i=tSize
3.1从存储字符及其权值得链表中取出两个权值最小得结点x,y,记录其下标x,y。
3.2将下标为x与y得哈夫曼树得结点得双亲设置为第i个结点
3.3将下标为x得结点设置为i结点得左孩子,将下标为y得结点设置为i结点得右孩子,i结点得权值为x结点得权值加上y结点得权值,i结点得双亲设置为空
4、根据哈夫曼树创建编码表
源代码:
void HuffmanTree:
:
CreateHTree(Node*p,int n)
{
root=new BiNode[2*n-1]; //初始化哈夫曼树
ﻩNode *front=p->next;
if(n==0)
ﻩthrowexception("没有输入字符");
for(inti=0;i<n;i++) //将n个字符得权值存储在哈夫曼树数组得前n位
{
ﻩroot[i]、data=front->count;
root[i]、lchild=-1;
ﻩroot[i]、rchild=-1;
ﻩroot[i]、parent=-1;
ﻩfront=front->next;
}
ﻩfront=p;
intNew1,New2;
for(i=n;i<2*n-1;i++)
{
SelectMin(New1,New2,0,i); //从0~i中选出两个权值最小得结点
root[New1]、parent=root[New2]、parent=i;//用两个权值最小得结点生成新结点, 新节点为其双亲
root[i]、data=root[New1]、data+root[New2]、data;//新结点得权值为其孩子得权值得与
ﻩ root[i]、lchild=New1;
root[i]、rchild=New2;
ﻩroot[i]、parent=-1;
}
ﻩCreateCodeTable(p); //创建编码表
}
时间复杂度:
在选取两个权值最小得结点得函数中要遍历链表,时间复杂度为O(n),故该函数得时间复杂度为O(n^2)
3.创建编码表(voidHuffmanTree:
:
CreateCodeTable(Node*p))
算法伪代码:
1、初始化编码表
2、初始化一个指针,从链表得头结点开始,遍历整个链表
2、1将链表中指针当前所指得结点包含得字符写入编码表中
2、2 得到该结点对应得哈夫曼树得叶子结点及其双亲
2、3如果哈夫曼树只有一个叶子结点,将其字符对应编码设置为0
2、4如果不止一个叶子结点,从当前叶子结点开始判断
2、4、1 如果当前叶子结点就是其双亲得左孩子,则其对应得编码为0,否则为1
2、4、2child指针指向叶子结点得双亲,parent指针指向child指针得双亲,重复2、4、1得操作
2、5将已完成得编码倒序
2、6取得链表中得下一个字符
3.输出编码表
源代码:
voidHuffmanTree:
:
CreateCodeTable(Node*p)
{
HCodeTable=newHCode[tSize]; //初始化编码表
Node*front=p->next;
for(inti=0;i {
ﻩﻩHCodeTable[i]、data=front->character; //将第i个字符写入编码表
ﻩintchild=i; //得到第i个字符对应得叶子节点
ﻩ intparent=root[i]、parent; //得到第i个字符对应得叶子节点得双亲
intk=0;
ﻩ if(tSize==1) //如果文本中只有一种字符,它得编码为0
{
ﻩﻩ HCodeTable[i]、code[k]='0';
ﻩk++;
}
ﻩwhile(parent!
=-1) //从第i个字符对应得叶子节点开始,寻找它到根结点得路径
ﻩ{
ﻩif(child==root[parent]、lchild) //如果当前结点为双亲得左孩子,则编码为0, 否则编码为1
HCodeTable[i]、code[k]='0';
else
HCodeTable[i]、code[k]='1';
ﻩﻩk++;
child=parent;
ﻩparent=root[child]、parent;
ﻩﻩ}
ﻩHCodeTable[i]、code[k]='\0';
Reverse(HCodeTable[i]、code); //将编码逆置
ﻩ front=front->next;//得到下一个字符
ﻩ}
ﻩ cout<<"编码表为:
"< for(i=0;i<tSize;i++) //输出编码表
ﻩ {
ﻩ cout<}
}
时间复杂度:
需要遍历哈夫曼树获取编码,时间复杂度为O(n^2)
4.选择两个最小权值得函数
算法伪代码:
1.从下标为begin得结点开始,寻找第一个没用过得结点
2.遍历哈夫曼树中从下标为begin到下标为end得结点序列,寻找没用过得同时权值又就是最小得结点。
3.暂时改变找到得权值最小结点得双亲域,防止第2次找到相同得结点。
4.将权值最小结点得下标记录下来。
5.重复步骤1~4,找到第2个权值最小得结点
源代码:
voidHuffmanTree:
:
SelectMin(int &New1,int&New2,int begin,intend)
{
intmin;
ﻩfor(intj=0;j<2;j++) //要选择两个权值最小得结点
{
ﻩintsign=begin;
ﻩfor(int i=begin;i<end;i++)//从下标为begin得结点开始,寻找第1个没用过得结点
ﻩ{
ﻩ if(root[i]、parent==-1) //没用过得结点其双亲应为空
ﻩ{
ﻩ min=root[i]、data;
ﻩﻩsign=i;
ﻩﻩﻩ break;
ﻩﻩ}
ﻩﻩ}
for(i=begin;i<end;i++) //从begin到end,寻找权值最小得没用过得结点
ﻩﻩ{
if(root[i]、parent==-1)
ﻩ{
ﻩﻩﻩﻩif(min>root[i]、data)
{
ﻩﻩmin=root[i]、data;
ﻩﻩsign=i;
ﻩﻩﻩ}
ﻩﻩ}
ﻩﻩ}
root[sign]、parent=0;//暂时改变所找最小结点得双亲域,防止第2次找到得就是同一个结点
ﻩif(!
j)
ﻩNew1=sign;
ﻩelse
ﻩﻩNew2=sign;
ﻩ}
}
时间复杂度:
两次遍历链表,时间复杂度为O(n)
5、将字符串倒序得函数(voidHuffmanTree:
:
Reverse(char*pch))
算法伪代码:
1.得到字符串得长度
2.初始化两个记录下标得变量,一个为字符串开头字符所在得下标i,另一个为字符串结尾字符所在得下标j
3.将下标为i与j得字符交换
4.i++,j--
时间复杂度:
时间复杂度为O(n)
6、编码函数(void HuffmanTree:
:
Encode(string&s,string&d))
算法伪代码:
1、从s开头得字符开始,逐一对s中得字符进行编码
2、在编码表中查找与当前字符对应得字符
3.如果找到了与当前字符对应得编码表中得字符,将其编码追加到解码串得末尾。
4、重复以上步骤,直到所有待编码串中得字符都编码完毕
5、输出编码后得字符串
源代码:
voidHuffmanTree:
:
Encode(string&s,string&d)
{
for(intj=0;j
{
for(inti=0;iﻩ{
ﻩif(s[j] ==HCodeTable[i]、data)
ﻩﻩﻩ{
ﻩd、append(HCodeTable[i]、code);//编码
ﻩﻩﻩbreak;
ﻩﻩ}
ﻩ}
ﻩ}
cout<<d<}
时间复杂度:
设待编码字符串长度为n,编码表中字符个数为m,则复杂度为O(n*m)
7、解码函数
算法伪代码:
1.得到指向哈夫曼树得根结点得指针与指向待解码串中得第1个字符得指针
2.逐个读取待解码串中得字符,若为0,则指向哈夫曼树当前结点得指针指向当前结点得左孩子,若为1,则指向当前结点得右孩子
3.指向待解码串得指针指向解码串中得下一个字符,直到指向哈夫曼树结点得指针得孩子结点为空
4.如果哈夫曼树只有一个叶子结点,直接将待解码串中得编码转换为对应得字符
5.如果指向哈夫曼树结点得指针得孩子结点已经为空,则将叶子结点下标对应得字符追加到解码串中。
6.输出解码串
源代码:
voidHuffmanTree:
:
Decode(string&s,string &d)
{
ﻩ for(inti=0;i<s、length();)
ﻩ{
intparent=2*tSize-1-1; //得到哈夫曼树得根结点
ﻩ while(root[parent]、lchild!
=-1) //如果结点不为叶子结点
ﻩ {
ﻩﻩif(s[i]=='0') //编码为0则寻找其左孩子
ﻩﻩ parent=root[parent]、lchild;
ﻩﻩ else //编码为1则寻找右孩子
ﻩﻩ parent=root[parent]、rchild;
ﻩi++;
ﻩﻩ }
ﻩif(tSize==1) //如果编码表只有一个字符,则根结点即为叶子结点
ﻩ i++;
ﻩﻩ d、append(1,HCodeTable[parent]、data);//将叶子节点对应得字符追加到解码串中
}
cout<}
时间复杂度:
设待解码串长度为n,则复杂度为O(n)
8、计算哈夫曼编码得压缩比(voidHuffmanTree:
:
Calculate(strings1,strings2))
算法伪代码:
1.获得编码前字符串得长度,即其占用得字节数
2.获得编码后得字符串得长度,将其除以8然后向上取整,得到其占用得字节数
3.压缩比将两个相除
源代码:
voidHuffmanTree:
:
Calculate(strings1,strings2)
{
intcal1=s1、length();
int cal2=s2、length();
ﻩcal2=ceill((float)cal2/8); //将编码串得比特数转化为字节数
ﻩ cout<<"编码前得字符串长度:
"< cout<<"编码后得字符串长度:
"<<cal2< cout<<"压缩比为:
"<<((double)cal2/(double)cal1)*100<<"%"<}
时间复杂度:
O
(1)
9、打印哈夫曼树(voidHuffmanTree:
:
PrintTree(intTreeNode,int layer) )
算法伪代码:
1.如果待打印结点为空,则返回
2.递归调用函数打印当前结点得右子树
3.根据当前结点所在得层次确定其前面要输出多少空格,先输出空格,在打印当前结点得权值
4.递归调用函数打印当前结点得左子树
源代码:
void HuffmanTree:
:
PrintTree(intTreeNode,intlayer)
{
if(TreeNode==-1) //如果待打印结点为空,则返回
ﻩﻩreturn;
else
{
PrintTree(root[TreeNode]、rchild,layer+1);//先打印该结点得右子树,layer记录得就是该结点所在得层次
for(inti=0;icout<<'';
cout<<root[TreeNode]、data<<endl; //打印该结点得权值
PrintTree(root[TreeNode]、lchild,layer+1); //打印该结点得左子树
ﻩ}
}
时间复杂度:
中序遍历哈夫曼树,复杂度为O(n)
10、 菜单函数(voidHuffmanTree:
:
Menu())
算法伪代码:
1、逐一读取键盘缓存区中得字符,并将它们逐一追加到记录输入字符串得string变量中,直到读到回车输入符为止
2、删除string变量末尾得回车输入符
3.利用string变量创建哈夫曼树,初始化编码表。
4、直观打印哈夫曼树
5、 对输入得字符串进行编码
6、 对编码后得字符串进行解码
7、计算编码前后得压缩比并输出
源代码:
void HuffmanTree:
:
Menu()
{
cout<<"请输入您要编码得文本,按回车键确定输入"< stringInput;
char letter;
ﻩdo //将字符逐个读入Input变量中
ﻩ{
letter=cin、get();
Input、append(1,letter);ﻩ
ﻩ}while(letter!
='\n');
ﻩInput、erase(Input、length()-1,1); //去掉Input末尾得回车符
Init(Input); //根据输入得字符串创建哈夫曼树及其编码表
ﻩcout<<"直观打印哈夫曼树"<ﻩPrintTree(2*tSize-1-1,1); //打印哈夫曼树
cout<<'\n'<<'\n';
stringd1,d2;
cout<<"编码后得字符串为"<ﻩEncode(Input,d1); //编码并打印编码串
ﻩcout<<"解码后得字符串为"<<endl;
ﻩDecode(d1,d2); //解码并打印解码串
cout<<"ASCII码编码与HUFFMAN编码得比较"<<endl;
Calculate(Input,d1); //计算编码前后得压缩比
}
2、4 其她
1、由于题目要求能输入任意长得字符串,所以本程序采用了string变量来记录输入得字符串,并采用string类得类成员函数来完成各项任务
2、打印哈夫曼树时采用了递