第五章:树和二叉树之赫夫曼树及其编码

//已更改,之前是Select函数存在问题   选出的s1与s2大小不定,故结果显示不唯一。

#include <stdio.h>
#include <string.h>
#define ERROR 0
#define OK 1
#define Status int
#define N 1050

/*
构造赫夫曼树:n个初始结点,则赫夫曼树中会存在2n-1个结点,0号位置不使用的情况下,共需要2n个结点;
思想:
    1、初始化:将所有的单元中的双亲及其左右孩子的结点坐标都初始化为0;再输入前n个结点的值
    2、通过n-1次的选择、删除、合并来创建赫夫曼树:
        选择:从之前的所有结点中选择双亲为0,并且其权值为最小的两个;(这里构造一个Select函数来进行选择,选择结果放入s1与s2中)
        删除:将选中的两个结点的双亲改为非0;
        合并:将s1和s2的权值相加,作为一个新的结点权值,依次存入到数组的第n+1之后的单元中,同时记录这个新结点的左右孩子分别为s1,s2;并且双亲结点为0;
*/


/*
赫夫曼树的应用:赫夫曼编码:
    选择最优前缀编码的一种形式;
    另外,赫夫曼编码的实现是建立在已有赫夫曼树的基础上的;
    思想:
        从结点开始向根结点回溯,将数据倒叙临时存入一个一维数组中cd;start记录编码在cd中的起始位置;
        当到最终的根结点时,将cd中的变法复制到该字符相应的编码串中;

*/
//结点类型:
typedef struct LNode
{
    int number;
    int parent,l,r;
}LNode,*LinkList;
void Select(LNode f[],int t,int &s1,int &s2)
{
    //在前i-1个元素中选择出两个权值最小的;并且其双亲值为0;
    int tt=1,m;
    for(int i=1;i<=t;i++)
        if(f[i].parent==0)
        {
            if(tt==1) {s1=i;tt++;m=i;}
            else {s2=i;break;}
        }
    for(int i=1;i<=t;i++)
        if(f[i].number<f[s1].number && f[i].parent==0)
                s1=i;
    if(s1==s2) s2=m;
    for(int i=1;i<=t;i++)
        if(i!=s1 && f[i].number<f[s2].number && f[i].parent==0)
                s2=i;
    if(s1>s2)
    {int t=s1;s1=s2;s2=t;}
}
void Creat(LinkList &L,int n)
{
    //构造赫夫曼树:需要提前输入n个结点的值得大小;
    int m,i;
    m=2*n;
    L = new LNode[m];
    for(i=1;i<m;i++)
    {L[i].number=0;L[i].l=0;L[i].r=0;L[i].parent=0;}//初始化开设的数组;
    for(i=1;i<=n;i++)//输入前n个元素;
        scanf("%d",&L[i].number);
    //我是一个万恶的分割线--------------------------------------初始化工作完毕!
    for(i=n+1;i<m;i++)
    {
        int s1=1,s2=1;
        Select(L,i-1,s1,s2);
        //从数组L中的前i-1个中选择出来两个;
        L[i].parent=0;L[i].l=s1,L[i].r=s2;
        L[s1].parent=i;L[s2].parent=i;
        L[i].number=L[s1].number+L[s2].number;
        //新生节点的权值为左右孩子权值之和;
    }
}

//根据赫夫曼树构造赫夫曼编码:
void Creat1(char HC[100][100],LinkList &L,int n)
{
    //HC是新建的字符数组,用来存生成的每个编码;
    //L是之前创造好的郝夫曼树一维数组;
    /*HC = new char *[n+1];*/
    char *cd = new char[n];cd[n-1]='\0';//编码结束符;
    
    int i,j,start;
    for(i=1;i<=n;i++)
    {
        start=n-1;
        int c=i,f=L[i].parent;
        while(f!=0)
        {
            start--;
            if(L[f].l==c)    cd[start]='0';
            else    cd[start]='1';
            c=f;f=L[f].parent;
        }
        /*HC[i]=new char[n-start];*/
        strcpy(HC[i],&cd[start]);
        //int p=0;
        //for(j=start;j<n;j++)
        //    HC[i][p++]=cd[j];
    }
}

int main()
{
    int i,j,k,n;
    printf("请输入一个数字n:代表需要提前输入n个结点的权值:\n");scanf("%d",&n);
    //创建赫夫曼树;
    LinkList L;
    Creat(L,n);

    //
    char HC[100][100];
    Creat1(HC,L,n);

    for(i=1;i<2*n;i++)
    {
        printf("%s\n",HC[i]);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/songacm/p/3382433.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值