您的位置:首页 > 其它

霍夫曼树 编码

2016-05-07 17:23 176 查看
用数组存储二叉树,Fun_encrypt_leaf()由叶子开始至根部编码,这个存储编码串时从尾部开始存储,对编码好的字符串进行拷贝时正好倒置

过来;fun_encrypt_recursion()从根部开始递归到叶子进行编码;fun_encrypt_onrecurent()非递归无栈从根部到叶子进行编码。思想都很强大,我是码农— —

bbb……

#include<一坨头文件>
using namespace std;
#define new Pzjay
const int MAX=1<<25;
const int length=50;//假设每个字母编码后最长50位
typedef char** hufferman_code;
char *word;
struct node
{
int value;
int Lch,Rch,Dad;
};
void select(node *HT,int n,int &s1,int &s2)
{
int i;
int MIN_1=MAX,MIN_2=MAX;//分别记录最小的跟次小的,初始很大
//sort(HT,HT+n);
for(i=1;i<=n;++i)
if(0==HT[i].Dad)//Dad=0表示未被使用过
if(MIN_1>HT[i].value)
{
MIN_1=HT[i].value;
s1=i;
}
for(i=1;i<=n;++i)
if(0==HT[i].Dad)//Dad=0表示未被使用过
if(MIN_2>HT[i].value && i!=s1)
{
MIN_2=HT[i].value;
s2=i;
}
}
void initialize_tree(node *HT,int *w,int n)//初始化霍夫曼树,加密,构造hufferman树
{
int m,i;
m=n*2-1;
if(n<=1)
return;
for(i=1;i<=n;++i)
{
HT[i].value=w[i];
HT[i].Dad=HT[i].Lch=HT[i].Rch=0;
}
for(i=n+1;i<=m;++i)
HT[i].value=HT[i].Dad=HT[i].Lch=HT[i].Rch=0;//以上为初始化过程
printf("下面是构建hufferman树的过程/n/n");
int s1,s2;//记录每次取出的两个频率最小的结点
for(i=n+1;i<=m;++i)
{
select(HT,i-1,s1,s2);//得到节点最小的两个节点的序号,s1<=s2的
//cout<<"s1= "<<s1<<" s2= "<<s2<<endl;
HT[s1].Dad=HT[s2].Dad=i;
HT[i].Lch=s1;
HT[i].Rch=s2;
HT[i].value=HT[s1].value+HT[s2].value;
}
}
void encrypt_leaf(node *HT,hufferman_code &HC,int n)//开始编码加密---从叶子到根逆向编码
{
char *cd;
int start,c,f;
cd=Pzjay char
;
cd[n-1]='/0';
for(int i=1;i<=n;++i)
{
start=n-1;
for(c=i,f=HT[i].Dad;f!=0;c=f,f=HT[f].Dad)//从当前的结点向根节点回溯
{
if(HT[f].Lch==c)
cd[--start]='0';
else
cd[--start]='1';
}
HC[i]=Pzjay char [n-start];//第i个字符的编码长度为n-start
strcpy(HC[i],cd+start);
//printf("中间 %c: %s/n",word[i],cd);/////////////////////////////
}
delete cd;
}
int cd_len=0;//递归函数所用到的全局变量
void encrypt_recursion(int i,char *cd,node *HT,hufferman_code &HC)//递归编码,传递进节点编号i;从根节点开始编码
{
if(HT[HT[i].Dad].Lch==i)//当前节点是父亲的左儿子时
cd[cd_len++]='0';
else if(HT[HT[i].Dad].Rch==i)
cd[cd_len++]='1';//右儿子
if(0==HT[i].Rch && 0==HT[i].Lch)//编码完成,到达叶子节点
{
//cout<<"i= "<<i<<" ";
HC[i]=Pzjay char[cd_len+1];
cd[cd_len]='/0';//strcpy函数识别用
//cout<<word[i]<<" "<<cd<<endl;
strcpy(HC[i],cd);
--cd_len;
}
else
{
if(HT[i].Lch!=0)//还有左儿子,遍历之
{
encrypt_recursion(HT[i].Lch,cd,HT,HC);
}
if(HT[i].Rch!=0)//还有右儿子,回退一个,遍历之
{
--cd_len;
encrypt_recursion(HT[i].Rch,cd,HT,HC);
}
--cd_len;//
}
}
void encrypt_onrecurent(node *HT,hufferman_code &HC,int n)//非递归无栈编码
{
char *cd;
cd=Pzjay char
;
cd_len=0;//全局变量
int p=2*n-1,i;
for(i=1;i<=2*n-1;++i)
HT[i].value=0;//此时value数据域作用是标记,为0时向左儿子,1向右儿子,2回溯到父亲那
//初始是向左儿子遍历——先序遍历
while(p)//初始p=2*n-1,处于根节点
{
if(0==HT[p].value)//向左走
{
HT[p].value=1;//左儿子遍历完毕,下面就是遍历右儿子,提前标记之
if(HT[p].Lch!=0)//左儿子的左儿子……
{
p=HT[p].Lch;
cd[cd_len++]='0';
}
else if(0==HT[p].Rch)
{
HC[p]=Pzjay char [cd_len+1];
cd[cd_len]='/0';
strcpy(HC[p],cd);
//printf("中间 %c: %s/n",word[p],cd);//////////////////////////////
}
}
else if(HT[p].value==1)//向右走value写成了Rch////////////////
{
HT[p].value=2;//左右儿子都遍历过了,下面就是回到父亲那
if(HT[p].Rch!=0)//右儿子有后代
{
p=HT[p].Rch;
cd[cd_len++]='1';
}
}
else
{
HT[p].value=0;//回到父节点,下面是左儿子
p=HT[p].Dad;//回溯
--cd_len;
}
}
delete cd;
}
void printf_hufferman(hufferman_code HC,int n)
{
for(int i=1;i<=n;++i)
{
printf("%c: ",word[i]);
printf("%s/n",HC[i]);
}
}
int main()
{
int *frequency;//保存字符出现频率---扩大100倍
int i,n;
node *hufferman_tree;
hufferman_code HC;

scanf("%d",&n);//输入字符个数
getchar();

HC=Pzjay char* [n+5];
word=Pzjay char [n+5];
frequency=Pzjay int [n+5];//预留缓冲空间

for(i=1;i<=n;++i)
{
scanf("%c %d",word+i,frequency+i);//输入各个字符及相应的出现频率
getchar();
}
hufferman_tree=Pzjay node[2*n];//先进行地址空间申请,才能传递进构造树的函数
initialize_tree(hufferman_tree,frequency,n);//初始化

//for(int i=2*n-1;i>0;--i)//查看建好的树
//printf("%d L=%d R=%d Dad=%d/n",i,hufferman_tree[i].Lch,hufferman_tree[i].Rch,hufferman_tree[i].Dad);

encrypt_leaf(hufferman_tree,HC,n);//从叶子开始的编码
printf("从叶子开始编码的结果/n");
printf_hufferman(HC,n);//打印编码后的字符

char *cd=Pzjay char[n+5];
encrypt_recursion(2*n-1,cd,hufferman_tree,HC);//递归从根开始编码
printf("从根开始的递归编码的结果/n");
printf_hufferman(HC,n);
delete cd;

encrypt_onrecurent(hufferman_tree,HC,n);//非递归无栈编码
printf("非递归无栈编码的结果/n");
printf_hufferman(HC,n);

return 0;
}


测试数据和编码结果:

Input:

8

a 12

b 3

c 4

e 67

f 56

g 2

h 10

i 100

Output:

a: 110

b: 10100

c: 101011

e: 111

f: 1011

g: 101010

h: 100

i: 0

Input:

5

a 1

b 2

c 3

d 4

e 5

Output:

a: 010

b: 011

c: 00

d: 10

e: 11

Input:

3

a 1

b 2

c 4

Output:

a: 00

b: 01

c: 1

ps:根据建树实现细节的不同,编码结果可能大同小异,可以无视……可以无视……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  霍夫曼树