哈夫曼树(Huffman Tree)

本文详细介绍了霍夫曼编码的实现过程,包括选择未合并元素中权值最小的两个节点,构建霍夫曼树,以及遍历树得到叶子节点的编码。通过C++代码展示了如何动态分配内存,初始化节点,进行节点合并,最终生成并输出霍夫曼编码。
//
// Created by dgm on 19-3-16.
//
#include <iostream>
#include <cstring>
using namespace std;
typedef struct HNode{
    HNode(int w,int p,int l,int r)
    :weight(w),
    parent(p),
    lchild(l),
    rchild(r)
    {}
    unsigned int weight;
    unsigned int parent,lchild,rchild;
}HNode,*HuffmanTree;
typedef char** HuffmanCode;     //保存n个huffman编码
void Select(HuffmanTree HT,int n,int &s1,int &s2)   //从未被合并过得元素中找到权值最小的两个
{
    for (int i = 1; i <= n; ++i) {
        if(!HT[i].parent){
            s1=i;break;
        }
    }
    for (int i = s1+1; i <= n; ++i) {
        if(!HT[i].parent){
            s2=i;break;
        }
    }
    for (int i = s2; i <= n; ++i) {
        if(!HT[i].parent&&HT[i].weight<=HT[s1].weight){
            s2=s1;s1=i;
        }
    }
}
void HuffmanCoding(HuffmanTree&HT,HuffmanCode&HC,int *w,int n)
{
    int m=2*n-1;
    HT=(HuffmanTree)malloc((m+1)* sizeof(HNode));   //分配空间,由于第一个元素保存在HT[1]而非HT[0]中,
                                                    //所以申请m+1个空间
    HuffmanTree p;
    int i=0;
    for (i=1,p=HT+1;i<=n;i++,p++,w++) *p={*w,0,0,0};    //元素保存在叶子节点中(编号1-n)
    for (;i<=m;i++,p++) *p={0,0,0,0};                   //非叶节点(n+1-m)
    for (i=n+1; i<=m ; ++i) {             //从第一个非叶节点开始
        int s1,s2;
        Select(HT,i-1,s1,s2);
        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((n+1)* sizeof(char*));   //HC[i]存储第i个元素的编码
    char *cd=(char*)malloc(n* sizeof(char));        //cd作临时变量,记录编码后存入HC

    for (i=1; i<=n; i++) {
        cd[n-1]='\0';   //后边的strcpy是以cd[start]的地址为参数,所以要标记结束位置
        int c,f;
        int start=n-1;
        //下边是从树的最底层(或者叶子)回到根节点,并在这个过程中得到编码
        for (c=i,f=HT[i].parent; f; c=f,f=HT[f].parent) {//从第一个元素开始
            if (HT[f].lchild==c)cd[--start]='0';   //如果元素是爸爸的左孩子,则标0
            else cd[--start]='1';                  //如果是右孩子,则标1
            //因为是从叶子到根,所以--start(前置--),这样以后,按从cd[start]到cd[n-1]的顺序
            //得到的恰好是按从根到叶子的编码(而不是从叶子到根)
        }
        HC[i]=(char*)malloc((n-start)* sizeof(char));   //实际编码位数所需空间
        strcpy(HC[i],&cd[start]);       //复制到HC[i]
    }
    free(cd);   //过河拆桥
    for (i = 1; i <= n; ++i) {  //输出看看,应该正确
        cout<<"the Huffman Code of "<<i<<" is "<<HC[i]<<endl;
    }
}
int main()
{
    //这里为了简便,将元素的值和权重设成一样的了,
    //也可以用一个结构体分别保存值和权重(例如值为E,权重为0.7)
    //然后按照权重对值进行编码    
    int n=5;
    int *w;
    for (int i = 1; i <= n; ++i) {
        w[i]=i;
    }
    HuffmanTree HT;
    HuffmanCode HC;
    HuffmanCoding(HT,HC,w,n);
    return 0;
}
在C语言中实现哈夫曼通常涉及到链表和优先队列的数据结构。以下是一个简单的步骤说明: 1. **创建节点结构**: ```c typedef struct Node { char symbol; int freq; struct Node* left; struct Node* right; } Node; ``` 2. **初始化数据结构**: 创建一个包含所有字符及其频率的数组或链表。 3. **构建优先队列**: 使用堆(Priority Queue)数据结构存储节点,`freq`作为比较依据,优先级低的节点先入堆。 4. **构造哈夫曼**: - 当堆中只剩下一个元素时,该元素即为根节点,结束构建。 - 从堆中取出两个频率最低的节点,合并它们为新的节点,并更新其频率为其子节点的频率之和。 - 将新节点插入堆中,并替换掉原来的两个小节点。 5. **编码过程**: 遍历生成的哈夫曼,为每个字符分配一个由'0'和'1'组成的编码。 6. **实现细节**: 使用`malloc`动态分配内存,以及`heapify`等函数维护堆的性质。 这里只给出了大致的框架,实际编写代码时需要考虑错误处理、循环终止条件以及具体的编码操作。完整的实现会比这个描述更复杂一些。 ```c #include <stdio.h> #include <stdlib.h> // ... 其他辅助函数 Node* create_node(char symbol, int freq) { // 创建节点并初始化... } void heapify(Node** heap, int size, int i) { // 实现最小堆... } int main() { // 初始化字符和频率... // 构建堆... // 合并节点... // 编码... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值