现在的位置: 首页 > 综合 > 正文

哈夫曼树的实现(C语言)

2013年01月08日 ⁄ 综合 ⁄ 共 2961字 ⁄ 字号 评论关闭
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

#define     OK            1
#define     ERROR         0
#define     OVERFLOW     -1

#define     Status       int

//哈夫曼树节点类型定义
typedef  struct  HTNodew
{
    unsigned  int weight;//权重
    unsigned  int parent,lchild,rchild;//父节点,左孩子,右孩子(无符号类型)
}HTNode,*HuffmanTree;

typedef char **  HuffmanCode;

HuffmanTree HT;

//选取最小的二个序列
Status Select(int n,int *s1,int *s2,int *foot);

//构造哈夫曼树
Status  HuffmanCoding(HuffmanCode *HC, int  n,unsigned int *w)
{
    /*
    **w存放n个字符的权值(均大于0),构造哈夫曼树HT,并且求出n个字符的哈夫曼编码HC
    */
    if(n<=0)
        return ERROR;
    int m=2*n-1;           //m是总共需要的节点个数

    HT = (HuffmanTree)malloc(sizeof(HTNode)*(m+1));      //0号单元不使用,下标从1开始到m结束
    int  i=1;
    HuffmanTree p;
    for(p = HT,p++;i<=n;i++,p++,w++)//依次给节点赋值(这里首先p++是因为下标从1开始)
    {
        (*p).weight = *w;//初始父节点,左孩子节点和右孩子节点均为0
        (*p).parent = 0;
        (*p).lchild = 0;
        (*p).lchild = 0;
    }

    for(;i<= m;i++,p++)              //给后面的n-1个节点赋值
    {
        (*p).weight = 0;//初始父节点,左孩子节点和右孩子节点均为0
        (*p).parent = 0;
        (*p).lchild = 0;
        (*p).lchild = 0;
    }
    //设置一个m长度的数组记录节点是否被访问过
    int *foot=(int *)malloc(sizeof(int)*(m+1));//(下标也是从1开始,为了和上面的正好对应)
    int *s=foot;
    s++;
    int k;
    for( k=1;k<=m;k++)
    {
        *(s++)=0;//初始话为0 ,当有访问过,则赋值1,表示已经访问过了
    }
    /*
    **构建哈夫曼树
    */
    for(i = n+1;i<=m;i++)   //依次给后面的n-1个节点赋值(从n+1到m)
    {
        /*
        **在HT[1...i-1]选择parent为0 且weight最小的二个节点,其序号为s1和s2
        */
        int *s1 = (int *)malloc(sizeof(int ));
        int *s2 = (int *)malloc(sizeof(int));
        if(!s1||!s2)
        {
            printf("分配内存空间错误.\n");
            return ERROR;
        }

        Select(i-1,s1,s2,foot);
        HT[*s1].parent = i;
        HT[*s2].parent = i;
        HT[i].lchild = *s1;
        HT[i].rchild = *s2;
        HT[i].weight = HT[*s1].weight + HT[*s2].weight;

    }



    //-----从叶子到根节点逆向求每个字符的哈夫曼编码---------//

    *HC=(HuffmanCode)malloc(sizeof(char *)*(n+1));//从1开始,抛弃下标1

    char *cd = (char *)malloc(sizeof(char)*n);   //分配求编码的工作区间(n个字符最长的编码需要n-1个空间,加上结束符号)
    cd[n-1]='\0';//编码结束符

    int  start;//用来跟踪位置
    for(i = 1;i<=n;i++)//逐个字符求哈夫曼编码
    {
        start = n-1;//编码结束符位置
        int c,f;
        start--;
        for(c = i,f = HT[i].parent;f != 0;c = f,f = HT[f].parent)
        {
            if(HT[f].lchild == c)
                cd[start] = '0';
            else
                cd[start] = '1';
            start-=1;
        }
        start+=1;//这里一定要加1  否则多分配了内存空间
        (*HC)[i]=(char *)malloc((n-start)*sizeof(char));//分配n-start个char空间即可
        strcpy((*HC)[i],&cd[start]);
        char *p=(*HC)[i];
        printf("HuffManNode of %d is(from root to child) :\n",HT[i].weight);
        printf("%d:------>%s ",HT[i].weight,p);

        printf("\n");

    }
    free(cd);
    return OK;
}


Status Select(int n,int *s1,int *s2,int *foot)
{
    int i,j;
    int min=65536;
    int temp;

    for(i=1;i<=n;i++)//找到第一个最小的值的下标
    {
        if(HT[i].weight<min&&foot[i]==0)
        {
            min=HT[i].weight;
            temp=i;//记录最终的最小值的下标
        }
    }
    foot[temp]=1;
    *s1=temp;


    min=65536;
    for(j=1;j<=n;j++)//寻找最小节点下标
    {
        if(HT[j].weight<min&&j!=(*s1)&&foot[j]==0)
        {
            min=HT[j].weight;
            temp=j;
        }
    }
    foot[temp]=1;
    *s2=temp;
    //交换*s1和*s2,让*s1指向小的坐标  *s2指向大的坐标
    temp=*s1;
    *s1=*s2;
    *s2=temp;
    return OK;

}

int main()
{

    int n;
    printf("Input the number of weight:\n");
    scanf("%d",&n);
    unsigned int *w=(unsigned int *)malloc(sizeof(unsigned int)*n);
    printf("Input The weight!\n");

    unsigned s;
    unsigned *p=w;
    int i = 1;
    // unsigned int p[8]={5,29,7,8,14,23,3,11};
    while(i<=n)
    {
        scanf("%ud",&s);
        *p=s;
        i++;
        p++;
    }
    p=w;
    HuffmanCode *HC=(HuffmanCode *)malloc(sizeof(HuffmanCode));
    if(!HC)
    {
        printf("分配初始内存空间错误.\n");
        return 0;
    }
    HuffmanCoding(HC,n,p);
    return 0;
}

执行结果如下图所示:

觉得在本次编码过程中遇到的问题有如下两个小问题(虽然是小问题,但是浪费了很多很多时间和精力去调试):

1.数组下标问题,编码过程中数组下标是从0或者1开始看个人习惯),则对应的其他的和数组下标有关的变量 什么的 都需要仔细考虑,很容易越界去访问。感觉这种错误也是比较难调试的。希望以后可以注意!!~加速进步的步伐~

抱歉!评论已关闭.