数据结构哈夫曼树(C语言版)


一、 问题

在这里插入图片描述

需求分析

此图转载于账号u014447560(深海鲸歌)在这里插入图片描述

代码分析

结构体定义使用

需要定义树的结构,打印树的结构时需要用的队列

typedef struct node{
    char data;
    int weight;
    int parent;
    int lchild,rchild;
    int mark;		
}huffmanTree;

typedef  struct  code_N{
    char ch;
    char Bi_code[100];
}Char_coding;

typedef struct queue_node{
    int data;
    struct LinkNode *next;
}LinkNode;

typedef struct queue{
    struct queue_node* rear,*front;
}LinkQueue;
int allchar =0;     //记录所有字母的个数

建立哈夫曼树,首先需要找到两个权值最小的两个叶子结点,然后建树

初始化树


void find_min(huffmanTree *tree,int max,int *firstMin,int *secMin){
//查找两个最小的值
    int smallindex[2];
    for(int j = 0;j<2;j++){
        int temp = 9999;
        int count = 0;
        int final_index =0;
        for(int k = 0;k<=max;k++) {
            if(tree[k].mark==0){
                if(tree[k].weight<temp){
                    final_index = k;
                    temp = tree[k].weight;
                }
            }
            else{
                count++;
            }
//            最后一个结点
            if(count ==max &&j ==0){
                return ;
            }

        }
        tree[final_index].mark =1;
        smallindex[j] = final_index;
    }
    *firstMin = smallindex[0];
    *secMin =smallindex[1];

}
//建树
huffmanTree * Init_huffmanTree(huffmanTree *tree,char *ch,int *weight){

    int all_node;
    int count = strlen(ch);
    if(strlen(ch)<0){
        return NULL;
    }
    all_node = 2*count -1;
//    初始化哈夫曼树
    tree = (huffmanTree*) malloc((all_node)*sizeof (huffmanTree));
    for(int i = 0;i<count;i++){
        tree[i].data =ch[i];
        tree[i].weight = weight[i];
        tree[i].parent =-1;
        tree[i].lchild=-1;
        tree[i].rchild =-1;
        tree[i].mark =0;
    }
    for(int j = count;j<all_node;j++){
        tree[j].data ='#';
        tree[j].parent =-1;
        tree[j].weight =-1;
        tree[j].parent =tree[j].rchild=tree[j].lchild =-1;
        tree[j].mark =0;
    }
    for(int i = count;i<all_node;i++){
        int fristMin,secMin;
        fristMin = secMin = 0;
        find_min(tree,i-1,&fristMin,&secMin);       //找到最小索引的两个的下标
        tree[fristMin].parent = i;
        tree[secMin].parent = i;
        tree[i].weight = tree[fristMin].weight+tree[secMin].weight;
        tree[i].lchild = fristMin;
        tree[i].rchild = secMin;
    }

    return tree;        //返回初始化的哈夫曼树
}

哈夫曼编码(我采用的是从叶子结点–>根节点,所以实际是反过来的)

Char_coding * print_huffmancode(huffmanTree *tree,Char_coding *coding){
//    编码
    int ch_length = allchar;    //总数
    coding = (Char_coding*) malloc(sizeof (Char_coding)*(ch_length));
    Char_coding s;
    for (int i = 0;i<ch_length;i++) {
        int child_curr = i;
        int parent_curr = tree[i].parent;
        int j = 0;  //记录字符的索引
        int length_coding =0;       //记录编码的长度,在最后设置\000
        while (parent_curr != -1) {
            if (tree[parent_curr].lchild == child_curr) {
//                左子树设为0
                s.Bi_code[j] = '0';
                child_curr = parent_curr;
                parent_curr = tree[child_curr].parent;
                length_coding++;
                j++;
            } else {
//                右子树设为1
                s.Bi_code[j] = '1';
                child_curr = parent_curr;
                parent_curr = tree[child_curr].parent;
                length_coding++;
                j++;
            }
        }
        s.ch = tree[i].data;
        coding[i] = s;
//        处理串的末尾
        coding[i].Bi_code[length_coding] ='\000';
//        printf("%d %s\n",i, coding[i].Bi_code);
    }
    return coding;      //返回编码好的哈夫曼编码,从叶子到根节点
}

使用哈夫曼树译码(包括文件保存)

void Decoding(Char_coding *coding,huffmanTree *tree,char *f_name,char *fp_write){
//    译码
    FILE *fp1;
    FILE *fp_w;
    int length = allchar;
    fp_w = fopen(fp_write,"w+");
    fp1= fopen(f_name,"r");
    if(fp1 ==NULL){
        printf("译码文件不存在");
    }
    int root_index = 2*length-2;
    int child_root = root_index;
    while (!feof(fp1)){
        char temp = fgetc(fp1);
        if(temp!='\n'){
            if(temp == '0'){
                child_root = tree[child_root].lchild;
//                找到叶子结点
                if(tree[child_root].lchild ==-1 &&tree[child_root].rchild ==-1){
//                    printf("%c",tree[child_root].data);
                    fputc(tree[child_root].data,fp_w);
//                    回溯到根节点
                    child_root = root_index;
                }
            }
            else {
                if (tree[child_root].rchild != -1) {
                    child_root = tree[child_root].rchild;
                    if (tree[child_root].lchild == -1 && tree[child_root].rchild == -1) {
//                        printf("%c", tree[child_root].data);
                        fputc(tree[child_root].data,fp_w);
                        child_root = root_index;
                    }
                }
            }
        }
        else{
            continue;
        }
    }
    fclose(fp1);
    fclose(fp_w);
}

全部代码(测试文件之类的在下面的链接)

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

typedef struct node{
    char data;
    int weight;
    int parent;
    int lchild,rchild;
    int mark;
}huffmanTree;

typedef  struct  code_N{
    char ch;
    char Bi_code[100];
}Char_coding;

typedef struct queue_node{
    int data;
    struct LinkNode *next;
}LinkNode;

typedef struct queue{
    struct queue_node* rear,*front;
}LinkQueue;

int allchar =0;     //记录所有字母的个数

void find_min(huffmanTree *tree,int max,int *firstMin,int *secMin){
//查找两个最小的值
    int smallindex[2];
    for(int j = 0;j<2;j++){
        int temp = 9999;
        int count = 0;
        int final_index =0;
        for(int k = 0;k<=max;k++) {
            if(tree[k].mark==0){
                if(tree[k].weight<temp){
                    final_index = k;
                    temp = tree[k].weight;
                }
            }
            else{
                count++;
            }
//            最后一个结点
            if(count ==max &&j ==0){
                return ;
            }

        }
        tree[final_index].mark =1;
        smallindex[j] = final_index;
    }
    *firstMin = smallindex[0];
    *secMin =smallindex[1];

}

Char_coding * print_huffmancode(huffmanTree *tree,Char_coding *coding){
//    编码
    int ch_length = allchar;    //总数
    coding = (Char_coding*) malloc(sizeof (Char_coding)*(ch_length));
    Char_coding s;
    for (int i = 0;i<ch_length;i++) {
        int child_curr = i;
        int parent_curr = tree[i].parent;
        int j = 0;  //记录字符的索引
        int length_coding =0;       //记录编码的长度,在最后设置\000
        while (parent_curr != -1) {
            if (tree[parent_curr].lchild == child_curr) {
//                左子树设为0
                s.Bi_code[j] = '0';
                child_curr = parent_curr;
                parent_curr = tree[child_curr].parent;
                length_coding++;
                j++;
            } else {
//                右子树设为1
                s.Bi_code[j] = '1';
                child_curr = parent_curr;
                parent_curr = tree[child_curr].parent;
                length_coding++;
                j++;
            }
        }
        s.ch = tree[i].data;
        coding[i] = s;
//        处理串的末尾
        coding[i].Bi_code[length_coding] ='\000';
//        printf("%d %s\n",i, coding[i].Bi_code);
    }
    return coding;      //返回编码好的哈夫曼编码,从叶子到根节点
}

huffmanTree * Init_huffmanTree(huffmanTree *tree,char *ch,int *weight){

    int all_node;
    int count = strlen(ch);
    if(strlen(ch)<0){
        return NULL;
    }
    all_node = 2*count -1;
//    初始化哈夫曼树
    tree = (huffmanTree*) malloc((all_node)*sizeof (huffmanTree));
    for(int i = 0;i<count;i++){
        tree[i].data =ch[i];
        tree[i].weight = weight[i];
        tree[i].parent =-1;
        tree[i].lchild=-1;
        tree[i].rchild =-1;
        tree[i].mark =0;
    }
    for(int j = count;j<all_node;j++){
        tree[j].data ='#';
        tree[j].parent =-1;
        tree[j].weight =-1;
        tree[j].parent =tree[j].rchild=tree[j].lchild =-1;
        tree[j].mark =0;
    }
    for(int i = count;i<all_node;i++){
        int fristMin,secMin;
        fristMin = secMin = 0;
        find_min(tree,i-1,&fristMin,&secMin);       //找到最小索引的两个的下标
        tree[fristMin].parent = i;
        tree[secMin].parent = i;
        tree[i].weight = tree[fristMin].weight+tree[secMin].weight;
        tree[i].lchild = fristMin;
        tree[i].rchild = secMin;
    }

    return tree;        //返回初始化的哈夫曼树
}

huffmanTree * Init_file_tree(huffmanTree *tree,char* fp_save){

    FILE *fp;
    fp = fopen(fp_save,"r");
    if(fp ==NULL){
        printf("初始化文件不存在:%s",fp_save);
        exit(0);
    }
    int count = 0;
    while(!feof(fp)){
        char temp[200];
        fgets(temp,200,fp);
        count++;
    }
//   根据文件进行初始化
    fclose(fp);
    tree = (huffmanTree *) malloc(sizeof (huffmanTree)*(count-1));
    fp = fopen(fp_save,"r");
    for(int i = 0;i<count-1;i++){
        fscanf(fp,"%c,%d,%d,%d,%d,%d\n",&tree[i].data,&tree[i].weight,
               &tree[i].parent,&tree[i].lchild,&tree[i].rchild,&tree[i].mark);
    }
    allchar = (count+1)/2;
//    printf("%c",tree[0].data);
    return tree;        //返回初始化的哈夫曼树
}

void set_tree(huffmanTree *tree,char *file_save){
    int length = allchar;
    int node_len = 2*length -1;
    FILE *fp_save;
    fp_save = fopen(file_save,"w+");
    if (fp_save==NULL){
        printf("文件打开错误");
        exit(0);
    }
    for(int i = 0; i<node_len;i++){
        fprintf(fp_save,"%c,%d,%d,%d,%d,%d\n",tree[i].data,tree[i].weight,tree[i].parent,tree[i].lchild,tree[i].rchild,tree[i].mark);
//        printf("%c %d %d %d %d\n",tree[i].data,tree[i].parent,tree[i].lchild,tree[i].rchild,tree[i].mark);
    }
    fclose(fp_save);
}

void seting_file(Char_coding *coding,huffmanTree *tree,char *word,char *f_name){
//    保存目标文本编码到文件
    int length  = allchar;
    int char_length = strlen(word);
    FILE *fp;
    fp = fopen(f_name,"w+");
    if(fp==NULL){
        printf("文件打开错误");
        exit(0);
    }
    unsigned long count_line = 0;
    for(int i = 0;i<char_length;i++){
        for(int j = 0;j<length;j++){
            if(coding[j].ch == word[i]){
                for(int k =strlen(coding[j].Bi_code)-1;k>=0;k--){
//                    printf("%c",coding[j].Bi_code[k]);
                    fputc(coding[j].Bi_code[k],fp);
//                    在写入文件时50个换行
//                    if((count_line+1)%50==0){
//                        fputc('\n',fp);
//                    }
                    count_line++;
                }
                break;
            }
        }
    }
    fclose(fp);
}

void Decoding(Char_coding *coding,huffmanTree *tree,char *f_name,char *fp_write){
//    译码
    FILE *fp1;
    FILE *fp_w;
    int length = allchar;
    fp_w = fopen(fp_write,"w+");
    fp1= fopen(f_name,"r");
    if(fp1 ==NULL){
        printf("译码文件不存在");
    }
    int root_index = 2*length-2;
    int child_root = root_index;
    while (!feof(fp1)){
        char temp = fgetc(fp1);
        if(temp!='\n'){
            if(temp == '0'){
                child_root = tree[child_root].lchild;
//                找到叶子结点
                if(tree[child_root].lchild ==-1 &&tree[child_root].rchild ==-1){
//                    printf("%c",tree[child_root].data);
                    fputc(tree[child_root].data,fp_w);
//                    回溯到根节点
                    child_root = root_index;
                }
            }
            else {
                if (tree[child_root].rchild != -1) {
                    child_root = tree[child_root].rchild;
                    if (tree[child_root].lchild == -1 && tree[child_root].rchild == -1) {
//                        printf("%c", tree[child_root].data);
                        fputc(tree[child_root].data,fp_w);
                        child_root = root_index;
                    }
                }
            }
        }
        else{
            continue;
        }
    }
    fclose(fp1);
    fclose(fp_w);
}

//入队
void InQueue(LinkQueue *queue,int x){
//生成新节点
    LinkNode *p = (LinkNode*) malloc(sizeof (LinkNode*));
    p->data = x;
    p->next = NULL;
    if(queue->front ==NULL){
        queue->front = queue->rear = p;
    }
    else {
        queue->rear->next = p;
        queue->rear = p;
    }
}
//出队列
int DeQueue(LinkQueue *queue){
    if(queue->front ==NULL){
        return 0;
    }
    LinkNode *p = queue->front;
    int temp = p->data;        //保存出队列的值
    queue->front = p->next;
//    free(p);
    return temp;
}

void init_queue(LinkQueue *queue){
    queue->front = queue->rear =NULL;
}


void traverse_tree(huffmanTree*tree){
//    层次遍历
    LinkQueue queue;
    init_queue(&queue);
    int length = allchar;
    int all_node = 2*length-1;
    int count = all_node;
    int root_index = all_node-1;
    int ceng_index = root_index;
    int cennum =1;
    printf("下面是按照凹入表示法打印的哈夫曼树");
    while(count>0){
        for(int i = 0;i<cennum;i++){
            printf("-");
        }
        printf("%c\n",tree[root_index].data);
        count--;
        int l_child_index = tree[root_index].lchild;
        int right_index = tree[root_index].rchild;
        if(l_child_index!=-1){
            InQueue(&queue,l_child_index);
        }
        if(ceng_index == root_index){
            cennum++;
//            printf("\n");
            if(l_child_index !=-1 &&right_index !=-1 ){
                ceng_index = right_index;
            }
            else if(l_child_index ==-1 && right_index !=-1){
                ceng_index = right_index;
            }
            else if(l_child_index !=-1&& right_index ==-1){
                ceng_index = l_child_index;
            }
            else{
                ceng_index = tree[tree[tree[root_index].parent].lchild].rchild;
            }

        }
        if(right_index!=-1){
            InQueue(&queue,right_index);

        }
        root_index = DeQueue(&queue);
    }

}

void showface() {
    printf("\n\n");
    printf("\t\t\t====================哈夫曼树译码系统====================\n");
    printf("\t\t\t*                                                      *\n");
    printf("\t\t\t*         1>.初始化哈夫曼树 \n");
    printf("\t\t\t*         2>. 编码\n");
    printf("\t\t\t*         3>. 译码\n");
    printf("\t\t\t*         4>. 打印字符编码文件信息\n");
    printf("\t\t\t*         5>. 打印哈夫曼树\n");
    printf("\t\t\t*         0>. 退出本系统\n");
    printf("\t\t\t*         欢迎使用本系统!\n\n");
    printf("\t\t\t=========================================================\n");
}

huffmanTree * input_data(huffmanTree *tree){
    int num;
    printf("请输入字符总数\n");
    scanf("%d",&num);
    allchar = num;
    int weight[27] = {186, 64, 13, 22, 32, 103, 21,15, 47, 57 ,1 ,5, 32,20 ,57, 63, 15, 1 ,48 ,51, 80, 23 ,8, 18 ,1, 16, 1};
    char ch[27][2] = {' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
    tree= Init_huffmanTree(&tree,ch,&weight);       //初始化哈夫曼树
    return  tree;
}

void printf_code_print(char *filename){
    FILE *fp;
    fp = fopen(filename,"r");
    if(fp==NULL){
        printf("文件不存在");
        exit(0);
    }
    int count=0;
    while(!feof(fp)){
        char temp = fgetc(fp);
        if(temp !=EOF){
            printf("%c",temp);
        }

        if((count+1)%50 ==0){
            printf("\n");
        }
        count++;
    }
}

int main() {
    int button;
    huffmanTree *tree;
    char word[200];
    int num;
    Char_coding *coding;
    int isinit = 0;

    char fp_save[200] ="../save_tree.txt";
    char fp [200]="../codeing.txt";
    char fp_w [200] = "../textfile.txt";
    do {
        showface();
        printf("请输入相应的数字,进行相应的操作:\n");
        scanf("%d", &button);
        switch (button) {
            case 1:
                tree = input_data(&tree);
                set_tree(tree,fp_save);
                isinit =1;
                break;

            case 2:
                if(isinit ==0){
                    isinit =1;
                    tree = Init_file_tree(&tree,fp_save);
                }
                printf("请输入你想要编码的字符串\n");
                scanf("%s",word);
                coding =print_huffmancode(tree,&coding);
                seting_file(coding,tree,word,fp);
                break;

            case 3:
                if(isinit ==0){
                    isinit =1;
                    tree = Init_file_tree(&tree,fp_save);
                }
                Decoding(coding,tree,fp,fp_w);

                break;

            case 4:
               if(isinit ==0){
                    isinit =1;
                    tree = Init_file_tree(&tree,fp_save);
                }
                printf_code_print(fp);
                break;

            case 5:
                if(isinit ==0){
                    isinit =1;
                    tree = Init_file_tree(&tree,fp_save);
                }
                traverse_tree(tree);
                break;
            case 0:
                printf("即将退出.....");
                exit(0);

            default:
                printf("您输入的指令不正确,请重新输入");
                break;
        }
        printf("\n\n");
    } while (button);

    return 0;
}

源码及文件

链接:https://pan.baidu.com/s/154ub53uLrhoXN4IYwn5gUw 
提取码:1314

总结

上述是哈夫曼树的编码和译码过程,本文的字母是自己已经固定了,但是基本不变,本人是菜鸡一枚,需要的可以自取,如果有问题,可以在评论区交流,或者私信我。

posted @ 2021-12-05 23:48  Leo哥coding~  阅读(124)  评论(0编辑  收藏  举报  来源