霍夫曼树 编码

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值