4.2c 哈夫曼编码_代码

 4.2c 哈夫曼编码_代码_哔哩哔哩_bilibili

 

// 哈夫曼编码译码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_CHAR 128
#define MAXN 256

// 哈夫曼树结点定义
typedef struct {
    char ch;    //叶结点存储字符
    int weight; //字符出现频次
    int parent, lchild, rchild;
} HTNode, HuffmanTree[MAXN];

// char*:字符串指针,存放一个字符对应的哈夫曼编码("0101")
// HuffCode:数组类型,每个元素都是char*
typedef char* HuffCode[MAX_CHAR];

//==============================================
// 小根堆定义(存放哈夫曼树结点下标,按weight比较)
typedef struct{
    int elem[MAXN]; //存放哈夫曼树结点下标
    int len;        //堆的大小
} HeapNode,*Heap;

// 向下调整:p位置元素下沉(按weight比较)
void SiftDown(Heap H, int p, int n, HuffmanTree HT){
    int e = H->elem[p];
    for(int i = 2*p; i <= n; i *= 2){
        if(i < n && HT[H->elem[i+1]].weight < HT[H->elem[i]].weight) i++;
        if(HT[e].weight <= HT[H->elem[i]].weight) break;
        H->elem[p] = H->elem[i];
        p = i; //p位置下沉
    }
    H->elem[p] = e; //堆中存放哈夫曼树结点下标
}

// 堆插入:堆尾插入元素,向上调整(按weight比较)
void HeapInsert(Heap H, int e, HuffmanTree HT){
    int i = ++H->len;
    for( ; i > 1; i /= 2){
        if(HT[H->elem[i/2]].weight <= HT[e].weight) break;
        H->elem[i] = H->elem[i/2];
    }
    H->elem[i] = e; //堆中存放哈夫曼树结点下标
}

// 堆删除:用堆尾替换堆顶,向下调整,返回堆顶元素
int HeapDelete(Heap H, HuffmanTree HT){
    if(H->len <= 0) return -1;
    int e = H->elem[1];
    H->elem[1] = H->elem[H->len]; //替换
    SiftDown(H, 1, --H->len, HT);
    return e;
}

//==============================================
// 构造哈夫曼树:用小根堆存取哈夫曼树的结点下标
// n:叶子数目,w[]:字符频次,chars[]:字符
void CreateHuffTree(HuffmanTree HT, int n, int w[], char chars[]){
    if(n <= 1) return;
    int m = 2 * n - 1; //总结点数
    Heap heap=(HeapNode*)malloc(sizeof(HeapNode));
    // 初始化全部结点
    for(int i = 1; i <= m; i++){
        HT[i].weight = 0;
        HT[i].ch = '\0';
        HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
    }
    // 叶子结点赋值
    for(int i = 1; i <= n; i++){
        HT[i].ch = chars[i-1];
        HT[i].weight = w[i-1];  
        HeapInsert(heap, i, HT); //哈夫曼树结点下标i插入堆
    }
    // 循环合并两个最小权树
    for(int i = n+1; i <= m; i++){
        int s1 = HeapDelete(heap, HT); //取出堆顶weight最小的哈夫曼树结点下标
        int s2 = HeapDelete(heap, HT); 
        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; //合并权值
        HeapInsert(heap, i, HT); //哈夫曼树结点下标i插入堆
    }
    free(heap);
}

// 生成哈夫曼编码(叶子向上回溯) 
void CreateHuffCode(HuffmanTree HT, HuffCode HC, int n){
    char *cd = (char*)malloc(n*sizeof(char));
    cd[n-1] = '\0';
    int c,f,start;
    for(int i = 1; i <= n; i++){
        start = n-1;
        c = i;
        f = HT[c].parent;
        while(f != 0){
            if(HT[f].lchild == c)
                cd[--start] = '0';
            else
                cd[--start] = '1';
            c = f;
            f = HT[c].parent;
        }
        unsigned char ch = HT[i].ch;
        HC[ch] = (char*)malloc((n - start) * sizeof(char));
        strcpy(HC[ch], cd + start); //复制字符串
    }
    free(cd);
}

// 字符串编码:输入原串,输出01比特串 
void EncodeString(HuffCode HC, char *src, char *codeStr){
    codeStr[0] = '\0';
    for(int i = 0; src[i] != '\0'; i++){
        strcat(codeStr, HC[(unsigned char)src[i]]); //追加字符串
    }
}

// 哈夫曼译码:01串还原原始字符串 
int DecodeString(HuffmanTree HT, int n, char *codeStr, char *resStr){
    int root = 2*n-1; //根节点下标
    int p = root;
    int idx = 0;
    for(int i=0; codeStr[i]!='\0'; i++){
        if(codeStr[i] == '0')
            p = HT[p].lchild;
        else if(codeStr[i] == '1')
            p = HT[p].rchild;
        else
            return -1; //非法字符

        // 叶子结点,读取字符
        if(HT[p].lchild == 0 && HT[p].rchild ==0){
            resStr[idx++] = HT[p].ch;
            p = root; // 返回根,继续译码下一个字符
        }
    }
    resStr[idx] = '\0';
    if(p != root) return -2; //编码串不完整
    return 1;
}

int main(){
    char inputStr[] = "DADADCF"; //ASCII码字符
    printf("原始字符串:%s\n",inputStr);
    
    //统计字符频次
    int freq[MAX_CHAR]={0};
    for(int i=0;inputStr[i]!='\0';i++){
        freq[(unsigned char)inputStr[i]]++;
    }
    //收集出现过的字符
    char chars[100];
    int w[100], n=0;
    for(int i=0;i<MAX_CHAR;i++){ //枚举128个字符
        if(freq[i]>0){
            chars[n] = (char)i; //字符
            w[n] = freq[i]; //字符出现频次
            n++; //字符数
        }
    }

    //构造哈夫曼树,生成哈夫曼编码
    HuffmanTree HT;
    HuffCode HC={NULL};
    CreateHuffTree(HT,n,w,chars); //构造哈夫曼树
    CreateHuffCode(HT,HC,n); //生成哈夫曼编码
    printf("\n====字符哈夫曼编码表====\n");
    for(int i=0;i<n;i++){
        unsigned char c = chars[i];
        printf("字符 %c  频次:%d  编码:%s\n", c, freq[c], HC[c]);
    } //例 A:HC[65]="10", C:HC[67]="110"

    //编码原字符串为01串
    char codeStr[1024]={0};
    EncodeString(HC,inputStr,codeStr);
    printf("\n字符串编码(01串): %s\n",codeStr);

    //译码,还原字符串
    char decodeStr[1024]={0};
    int ret = DecodeString(HT,n,codeStr,decodeStr);
    if(ret == 1)
        printf("\n译码还原字符串:%s\n",decodeStr);
    else
        printf("\n译码失败\n");

    return 0;
}

 

posted @ 2026-10-07 07:02  董晓  阅读(3)  评论(0)    收藏  举报