NOJ-哈夫曼编/译码器-西工大数据结构

    昨天老师讲完了哈夫曼书,回到宿舍就开始写,到今天才算是通过。题目如下:


    分析一下题目,首先要构建一个哈夫曼树,然后先输出编码,再根据编码进行译码并输出。

    构建哈夫曼树,就是取所有数据中权重最小的两个,组成一个小树,这个小树的权重就是这两个数据权重之和,然后反复进行这个操作(已成为叶节点的数据不再进行权重比较),就可以得到一课哈夫曼书。这里并没有用二叉链表来存储,而是使用了一种顺序结构(记录下左支右支和父节点位置),这样也可以实现。若输入:3   a   b   c   1   2   4,则可以构建出如下的哈夫曼树:


    而进行编码时只要找到需编码数据在树中位置,再循环判断当前节点是其父节点的左支还是右支,若是左支则记录0,右支记录1,直到当前节点为头结点,就可得到需编码数据编码的逆序,再将它反过来就是所求编码。a在上个哈夫曼树中编码如下图:


    而到解码时,只需要从头结点开始走,0走左支1走右支,直到走到叶节点为止,叶节点中数据就是所求数据。01在上个哈夫曼树中解码如下图:


    以下是我的实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct huffmanNode
{
    char data;
    int weight;
    int parent;
    int left;
    int right;
};

struct huffmanTree
{
    struct huffmanNode node[200];
    int n;
};

void run ();
void putInData (struct huffmanTree *T);
void clear (struct huffmanNode *node);
void createNewTree (struct huffmanTree *T);
int findTwoMin (struct huffmanTree *T,int *min1,int *min2,int *weight);
int getCode (int A[],struct huffmanTree *T);
void printCode (int A[],int length);
int getEnCode (int A[],char B[],struct huffmanTree *T,int length1);
void printEnCode (char B[],int length);

int main()
{
    run ();
    return 0;
}

void run ()
{
    struct huffmanTree T;
    putInData(&T);
    createNewTree (&T);

    int A[2000]={0},length1;
    length1=getCode (A,&T);
    printCode (A,length1);

    char B[2000]={0},length2;
    length2=getEnCode (A,B,&T,length1);
    printEnCode (B,length2);
}

void putInData (struct huffmanTree *T)
{
    int i,n;
    scanf ("%d",&(T->n));
    n=T->n;
    clear (&(T->node[0]));
    for (i=1;i<=n;i++)
    {
        clear (&(T->node[i]));
        getchar();
        T->node[i].data=getchar();
    }
    for (i=1;i<=n;i++)
    {
        scanf ("%d",&(T->node[i].weight));
    }
}

void clear (struct huffmanNode *node)
{
    node->data='\0';
    node->weight=0;
    node->parent=0;
    node->left=0;
    node->right=0;
}

void createNewTree (struct huffmanTree *T)
{
    int min1,min2,weight;
    while (findTwoMin (T,&min1,&min2,&weight))
    {
        ++(T->n);
        clear (&(T->node[T->n]));
        T->node[T->n].left=min1;
        T->node[T->n].right=min2;
        T->node[T->n].weight=weight;
        T->node[min1].parent=T->n;
        T->node[min2].parent=T->n;
    }
}

int findTwoMin (struct huffmanTree *T,int *min1,int *min2,int *weight)
{
    int i,n,m;
    int minWeight1=2147483647;
    int minWeight2=2147483647;
    for (i=1,n=T->n;i<=n;i++)
    {
        if (!(T->node[i].parent))
        {
            m=T->node[i].weight;
            if (m<minWeight2)
            {
                if (m>minWeight1)
                {
                    minWeight2=m;
                    *min2=i;
                }
                else
                {
                    minWeight2=minWeight1;
                    minWeight1=m;
                    *min2=*min1;
                    *min1=i;
                }
            }
        }
    }
    if ((minWeight1!=2147483647)&&(minWeight2!=2147483647))
    {
        *weight=T->node[*min1].weight+T->node[*min2].weight;
        return 1;
    }
    else
    {
        return 0;
    }
}

int getCode (int A[],struct huffmanTree *T)
{
    int length=0,n,i,j,parent;
    char s[200]={0};
    getchar ();
    gets (s);
    n=strlen (s);
    for (i=n-1;i>=0;i--)
    {
        for (j=1;j<=T->n;j++)
        {
            if (s[i]==T->node[j].data)
            {
                parent=j;
                break;
            }
        }
        while (T->node[parent].parent)
        {
            if (T->node[T->node[parent].parent].left==parent)
            {
                A[length]=0;
            }
            else
            {
                A[length]=1;
            }
            length++;
            parent=T->node[parent].parent;
        }
    }
    return length-1;
}

void printCode (int A[],int length)
{
    int i;
    for (i=length;i>=0;i--)
    {
        printf ("%d",A[i]);
    }
    printf ("\n");
}

int getEnCode (int A[],char B[],struct huffmanTree *T,int length1)
{
    int i,length2=0,cur,head;
    head=T->n;
    for (i=length1,cur=head;i>=0;i--)
    {
        if (A[i])
        {
            cur=T->node[cur].right;
            if (!(T->node[cur].right))
            {
                B[length2]=T->node[cur].data;
                length2++;
                cur=head;
            }
        }
        else
        {
            cur=T->node[cur].left;
            if (!(T->node[cur].left))
            {
                B[length2]=T->node[cur].data;
                length2++;
                cur=head;
            }
        }
    }
    return length2-1;
}

void printEnCode (char B[],int length)
{
    int i;
    for (i=0;i<=length;i++)
    {
        printf ("%c",B[i]);
    }
    printf ("\n");
}

    以下是各函数的注释:

void run ()
{
    struct huffmanTree T;
    putInData(&T);//输入数据
    createNewTree (&T);//构建哈夫曼树

    int A[2000]={0},length1;
    length1=getCode (A,&T);//得到编码
    printCode (A,length1);//输出编码

    char B[2000]={0},length2;
    length2=getEnCode (A,B,&T,length1);//得到解码
    printEnCode (B,length2);//输出解码
}

void putInData (struct huffmanTree *T)
{
    int i,n;
    scanf ("%d",&(T->n));//输入数据个数
    n=T->n;
    clear (&(T->node[0]));//节点初始化
    for (i=1;i<=n;i++)//循环输入数据
    {
        clear (&(T->node[i]));
        getchar();
        T->node[i].data=getchar();
    }
    for (i=1;i<=n;i++)//循环输入权重
    {
        scanf ("%d",&(T->node[i].weight));
    }
}
void clear (struct huffmanNode *node)
{
    node->data='\0';//初始化都设为0
    node->weight=0;
    node->parent=0;
    node->left=0;
    node->right=0;
}
void createNewTree (struct huffmanTree *T)
{
    int min1,min2,weight;
    while (findTwoMin (T,&min1,&min2,&weight))//如果能找出最小的两个权重的数据所在位置,就循环
    {
        ++(T->n);//创建新节点
        clear (&(T->node[T->n]));//新节点初始化
        T->node[T->n].left=min1;//新节点左支赋值
        T->node[T->n].right=min2;//新节点右支赋值
        T->node[T->n].weight=weight;//新节点权重赋值
        T->node[min1].parent=T->n;//最小权重的父赋值
        T->node[min2].parent=T->n;//第二小权重的父赋值
    }
}
int findTwoMin (struct huffmanTree *T,int *min1,int *min2,int *weight)
{
    int i,n,m;
    int minWeight1=2147483647;
    int minWeight2=2147483647;
    for (i=1,n=T->n;i<=n;i++)//循环判断每一个节点
    {
        if (!(T->node[i].parent))//若其无头结点(无头结点说明其未组成小树)
        {
            m=T->node[i].weight;
            if (m<minWeight2)//判断是否最小或第二小,并赋值
            {
                if (m>minWeight1)
                {
                    minWeight2=m;
                    *min2=i;
                }
                else
                {
                    minWeight2=minWeight1;
                    minWeight1=m;
                    *min2=*min1;
                    *min1=i;
                }
            }
        }
    }
    if ((minWeight1!=2147483647)&&(minWeight2!=2147483647))//如果找到
    {
        *weight=T->node[*min1].weight+T->node[*min2].weight;//权重赋值
        return 1;//返回真,循环继续
    }
    else/如果未找到
    {
        return 0;//返回假,循环停止
    }
}
int getCode (int A[],struct huffmanTree *T)//编码
{
    int length=0,n,i,j,parent;
    char s[200]={0};
    getchar ();
    gets (s);//得到需编码数据串
    n=strlen (s);//得到其长度
    for (i=n-1;i>=0;i--)//循环每一个数据元素
    {
        for (j=1;j<=T->n;j++)//在树中寻找到其位置
        {
            if (s[i]==T->node[j].data)
            {
                parent=j;
                break;
            }
        }
        while (T->node[parent].parent)//如果它无父,说明到达头结点,结束,否则继续循环
        {
            if (T->node[T->node[parent].parent].left==parent)//如果是左支
            {
                A[length]=0;//记录0
            }
            else//同上
            {
                A[length]=1;
            }
            length++;//编码记录数组长度加一
            parent=T->node[parent].parent;
        }
    }
    return length-1;//返回编码记录数组最后一个数据的位置
}
void printCode (int A[],int length)
{
    int i;
    for (i=length;i>=0;i--)//逆序输出编码记录数组
    {
        printf ("%d",A[i]);
    }
    printf ("\n");
}
int getEnCode (int A[],char B[],struct huffmanTree *T,int length1)//解码
{
    int i,length2=0,cur,head;
    head=T->n;
    for (i=length1,cur=head;i>=0;i--)//循环编码记录数组每一个数据
    {
        if (A[i])//如果是1
        {
            cur=T->node[cur].right;//走右支
            if (!(T->node[cur].right))//如果本节点是叶节点
            {
                B[length2]=T->node[cur].data;//记录解码数据
                length2++;//解码记录数组长度加一
                cur=head;//从头开始
            }
        }
        else//同上
        {
            cur=T->node[cur].left;
            if (!(T->node[cur].left))
            {
                B[length2]=T->node[cur].data;
                length2++;
                cur=head;
            }
        }
    }
    return length2-1;//返回解码记录数组最后一个数据的位置
}
void printEnCode (char B[],int length)
{
    int i;
    for (i=0;i<=length;i++)//输出解码记录数组
    {
        printf ("%c",B[i]);
    }
    printf ("\n");
}


    以上就是我的实现,希望给大家带来帮助。




    


猜你喜欢

转载自blog.csdn.net/qq_30180107/article/details/80005624