哈夫曼树及WPL计算的一种实现

哈夫曼树及WPL计算的一种实现

主要思路写在注释里了,用了两个数组,一个作为堆,一个作为保存最后树状结构地址的数组。我自己感觉可能有点复杂了,下次可以试试用结构体而不是二维数组来实现同样的功能。
(使用Code::Block 17.12编译)

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

typedef struct Haffmantree{
    struct Haffmantree* Right;
    int data;
    struct Haffmantree* Left;
}Haff;

typedef struct HaffHeap{
    Haff* pdata;//存储元素的动态数组
    int capacity;//容量
}HaffHeap;


//思路:两个数组,一个是包含所给数的最小堆HaffHeap[0](void*),一个是包含访问过的树的数组HaffHeap[1](void*),数组里面的元素都是Haff
//capacity是数组容量(固定)
//出堆:HaffHeap[0]中出堆一个元素,放入HaffHeap[1]中,并返回其地址
//入堆:HaffHeap[0]中增加一个元素,值是出堆两个元素之和,并附有左右儿子的指针
//结束:直到HaffHeap[0]只剩一个元素。其他元素全部通过指针链接到那一个元素中。

///出堆:HaffHeap[0]中出堆一个元素,放入HaffHeap[1]中,并返回其地址
Haff* OutHeap(HaffHeap** H){
    Haff temp=H[0]->pdata[1];//H[0]最小元素
    H[1]->pdata[H[1]->capacity++]=temp;//放入H[1]中


    H[0]->pdata[1]=H[0]->pdata[H[0]->capacity--];//H[0]最大元素放在首部
    //把换到第一位的pdata[size]放到合适的位置
    int i=1;
    for(;;){
        if(i>=H[0]->capacity){
            break;
        }
        if((H[0]->pdata[i].data)>(H[0]->pdata[2*i].data)){//如果父节点大于子节点,则交换
            if((H[0]->pdata[2*i+1].data)<(H[0]->pdata[2*i].data)){
                temp=H[0]->pdata[i];
                H[0]->pdata[i]=H[0]->pdata[2*i+1];
                H[0]->pdata[2*i+1]=temp;
                continue;
            }
            temp=H[0]->pdata[i];
            H[0]->pdata[i]=H[0]->pdata[2*i];
            H[0]->pdata[2*i]=temp;
        }else{
            break;
        }
        i=i*2;
    }
    return &H[1]->pdata[H[1]->capacity-1];
}

///建立一个哈夫曼树
Haff* BuildHaffTree(HaffHeap** H){
    Haff* NewHaff=malloc((H[0]->capacity)*sizeof(Haff));//NewHaff是包含capacity个Haff元素的数列
    int i=0;
    Haff* temp1;
    Haff* temp2;
    while(H[0]->capacity!=1){
        printf("\n--------------\n");
        temp1=OutHeap(H);
        temp2=OutHeap(H);
        printf("temp1=%d\n",temp1->data);
        printf("temp2=%d\n",temp2->data);
        NewHaff[i].data=temp1->data+temp2->data;
        NewHaff[i].Left=temp1;
        NewHaff[i].Right=temp2;
        printf("NewHaff=%d\n",NewHaff[i].data);
        EnterHeap(&NewHaff[i],H);
        printf("\n哈夫曼堆:\n");
        Output(H[0]);
        printf("\n哈夫曼数组:\n");
        OutputArray(H[1]);
        i++;
    }
    return &H[0]->pdata[1];
}

///创建两个二维哈夫曼数组(HaffHeap[0] HaffHeap[1]),并返回
HaffHeap** CreateHaffHeap(int capacity){
    int i=1;//char转int需要的权重
    int j=1;//全局的计数
    char c;char转int的char数
    int n=0;//char转int的int数
    int temp;
    HaffHeap** NewHaffHeap=malloc(2*sizeof(HaffHeap*));//新建一个数组指针 NewHaffHeap(HaffHeap*)
    NewHaffHeap[0]=malloc(sizeof(HaffHeap));//哈夫曼堆:  NewHaffHeap[0](HaffHeap*)
    NewHaffHeap[1]=malloc(sizeof(HaffHeap));//哈夫曼数组:NewHaffHeap[0](HaffHeap*)

    NewHaffHeap[0]->pdata=malloc((2*capacity)*sizeof(Haff));//给NewHaffHeap[0]新建capacity*2个Haff元素
    NewHaffHeap[1]->pdata=malloc((2*capacity)*sizeof(Haff));//给NewHaffHeap[1]新建capacity*2个Haff元素

    NewHaffHeap[0]->pdata[0].data=0;//哨兵

    for(int r=0;r<=capacity;r++){//初始化指针
        NewHaffHeap[0]->pdata[r].Left=NULL;
        NewHaffHeap[0]->pdata[r].Right=NULL;
        NewHaffHeap[1]->pdata[r].Left=NULL;
        NewHaffHeap[1]->pdata[r].Right=NULL;
    }

    NewHaffHeap[0]->capacity=capacity;//哈夫曼堆:  NewHaffHeap[0](HaffHeap*)
    NewHaffHeap[1]->capacity=0;//哈夫曼数组:NewHaffHeap[0](HaffHeap*)

    //创建一个无序的新堆
    printf("请依次输入%d个数以组成哈夫曼树\n",capacity);
    while((c=getchar())!=EOF){
        if(c=='\n'){
            printf("n=%d\n",n);
            NewHaffHeap[0]->pdata[j++].data=n;
            n=0;
            i=1;
            continue;
        }
        n=n*i+c-'0';
        i=10;
    }

    //给无序的新堆排序
    i=NewHaffHeap[0]->capacity/2;
    for(;i>0;i--){//从序号i处开始做“下沉”操作
        Sink(NewHaffHeap[0],i);
    }
    //Output(NewHaffHeap[0]);
    return NewHaffHeap;
}


///带数创建堆时用到的函数“下沉”
//如果节点比左右儿子都大,把节点和其左右子节点中较小的那个交换。。。直到为空
void Sink(HaffHeap* H, int n){
    int temp;
    if(2*n>H->capacity){//1. 没有子节点
        return;
    }else if(2*n==H->capacity){//2. 有且只有一个左儿子
        if(H->pdata[n].data>H->pdata[2*n].data){//2.1 子节点比原节点小
            temp=H->pdata[n].data;
            H->pdata[n].data=H->pdata[n*2].data;
            H->pdata[n*2].data=temp;
            return;
        }
    }else if(2*n<H->capacity){//3. 有左右儿子
        if(H->pdata[2*n].data<=H->pdata[2*n+1].data){//3.1 左儿子比较小
            if(H->pdata[n].data>H->pdata[n*2].data){//原节点比左儿子大
                temp=H->pdata[n].data;
                H->pdata[n].data=H->pdata[n*2].data;
                H->pdata[n*2].data=temp;
                Sink(H,n*2);
            }
        }else if(H->pdata[2*n].data>H->pdata[2*n+1].data){//3.2 右儿子比较小
            if(H->pdata[n].data>H->pdata[n*2+1].data){//原节点比右儿子大
                temp=H->pdata[n].data;
                H->pdata[n].data=H->pdata[n*2+1].data;
                H->pdata[n*2+1].data=temp;
                Sink(H,n*2+1);
            }
        }
    }
}

///以完全二叉树的形式输出动态数组
void Output(HaffHeap* H){
    printf("\n");
    int limt=1;
    int j=1;//总的计数值(1~size)
    int i=1;//每层树的计数值(1~limt)
    for(;;){

        for(;i<=limt;i++,j++){
            printf("[%d]%d ",j,H->pdata[j].data);
            if(j==H->capacity){
                return;
            }
        }
        i=1;
        printf("\n\n");
        limt=limt*2;
    }
    printf("\n");
}

///输出数组
void OutputArray(HaffHeap* H){
    printf("\n");
    int i=0;//总的计数值(1~size)
    for(;i<H->capacity;i++){
        printf("[%d]%d ",i,H->pdata[i].data);
    }
    printf("\n");
}

///一个哈夫曼树的先序遍历
void PreReadHaff(Haff* R){

    if(R==NULL){
        return -1;
    }
    printf("%d\n",R->data);
    PreReadHaff(R->Left);
    PreReadHaff(R->Right);
}



///入堆:HaffHeap[0]中增加一个元素并排序
void EnterHeap(Haff* NewHaff,HaffHeap** H){
    int i=++H[0]->capacity;
    Haff temp;
    H[0]->pdata[i]=*NewHaff;
    while(H[0]->pdata[i/2].data>NewHaff->data){
        temp=H[0]->pdata[i/2];
        H[0]->pdata[i/2]=H[0]->pdata[i];//pdata[i/2]:父节点
        H[0]->pdata[i]=temp;
        i=i/2;
    }
}

///求WPL路径长度
//思路:WPL=Σ深度*树值
int WPL(Haff* HTree,int depth){
    depth++;
    //printf("本轮HT=%d,深度=%d\n",HTree->data,depth);

    if(!HTree->Left&&!HTree->Right){//到头了
        printf("HT=%d,深度=%d\n",HTree->data,depth);
        return depth*HTree->data;
    }
    int WPL1=WPL(HTree->Left,depth);
    int WPL2=WPL(HTree->Right,depth);
    //printf("WPL=%d\n",WPL1+WPL2);
    return WPL1+WPL2;
}



int main()
{
    Haff* Haffmantree=BuildHaffTree(CreateHaffHeap(5));
    PreReadHaff(Haffmantree);
    printf("WPL=%d",WPL(Haffmantree,0));
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值