根据小慧JJ的课件完成,用数组存储二叉树,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[n]; 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 [n]; 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:根据建树实现细节的不同,编码结果可能大同小异,可以无视……可以无视……