zoukankan      html  css  js  c++  java
  • C语言实现 —— 哈夫曼编码

    今日一言:
    永远不需要解释你自己,
    因为喜欢你的人不需要
    不喜欢你的人不相信。

    C语言实现 —— 哈夫曼编码

    我已经被它肝得无话可说,
    这是第n次写了。


    代码

    /*********************************************************************************
     *
     * 哈夫曼编码
     * create: 2020年5月22日 16点42分
     * author: LOS(小鱼) 
     *
     * *******************************************************************************
     * 本程序所使用的数据结构:
     *     1. 双向链表 
     *     2. 二叉树
     * *******************************************************************************
     * 提示:
     *     同一条件下,所构造的哈夫曼树可以不同,
     *     本程序的哈夫曼树同时受字符在文本中的排列顺序和字符的权重影响 
     *********************************************************************************/
     
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>

    #define OFFSET 35

    typedef struct Node{

        char value;
        int weight;

    }Node;

    struct{
        Node nodes[200];
        int size;
    } Nodes; // 全部的节点数据存储在这里

    typedef struct TreeNode{
        Node *node;
        struct TreeNode *parent,*left,*right;
    }TreeNode; 

    struct{
        TreeNode nodes[200];
        int size;
    } TreeNodes; // 其实没必要存储,但有得管理是好事 

    /*********************************************************************************
     * 链表需要 
     ********************************************************************************/
     

    typedef struct Elem{
        TreeNode *tNode;
        struct Elem *prev,*next;
    }Elem; 

    struct{
        Elem elems[200];
        int size;
    } Elems; // 全部的链表数据存储在这里 ( 不销毁, 但会覆盖 ) 

    // 定义链表结构
    typedef struct {
        struct Elem *first;
        struct Elem *last;
        int size;
    } LinkList; 

    // 初始化链表
    void initLinkList( LinkList *list ){
        list->size = 0;


    // 获取表长 
    int getSizeL(LinkList *list){
        return list->size;
    }

    void addFirst(LinkList *list,Elem *elem){
        if( !getSizeL(list) ){
            list->first = elem;
            list->last = elem;
        }
        else {
            elem->prev = list->last;
            elem->next = list->first;
            list->last->next = elem;
            list->first->prev = elem;
            list->first = elem;
        }
        list->size++;
    }

    // 添加元素 
    void addLast(LinkList *list,Elem *elem){
        if( !getSizeL(list) ){
            list->first = elem;
            list->last = elem;
        } else {
            elem->prev = list->last;
            elem->next = list->first;
            list->last->next = elem;
            list->first->prev = elem;
            list->last = elem;
        }
        list->size++;
    }

    Elem * getElem(LinkList *listint index){
        int i ;
        Elem *elem;
        // 逐项访问 
        if ( index > list->size/2 ){
            elem = list->last;
            for ( i = list->size-1 ; i >= index ; i-- ){
                if( i == index ){
                    return elem;
                }
                elem = elem->prev;
            }
        } else {
            elem = list->first;
            for ( i = 0 ; i <= index ; i++ ){
                if( i == index ){
                    return elem;
                }
                elem = elem->next;
            }
        }
    }

    // 移除元素 
    void removeIndexL(LinkList *listint index){
        Elem *elem = getElem(list, index);
        elem->prev->next = elem->next;
        elem->next->prev = elem->prev;
        if( index == 0 ){
            list->first = elem->next;
        }
        list->size--;
    }

    void removeElemL(LinkList *list, Elem *e){
        int i;
        Elem *elem = list->first;
    //    while(e != elem ){
    //        elem = elem->next;
    //    }
    //    elem->prev->next = elem->next;
    //    elem->next->prev = elem->prev;
    //    if( list->first == elem ){
    //        list->first = elem->next;
    //    }
        int flag = 0;
        for ( i = 0 ; i < list->size ; i++ ){
            if( elem == e ){
                elem->prev->next = elem->next;
                elem->next->prev = elem->prev;
                iflist->first == elem ){
                    list->first = elem->next;
                }
                iflist->last == elem ){
                    list->last = elem->prev;
                }
                flag = 1;
            }
            elem = elem->next;
        }
        if(!flag) printf("没能完成任务!!! ");
        list->size--;
    }

    /*********************************************************************************
     * 链表需要 
     ********************************************************************************/
     

    void init(){
        Nodes.size = 0;
        Elems.size = 0;
        TreeNodes.size = 0;
    }

    // 新建节点,返回节点指针 
    Node *createNode(char value,int weight){
        Node *p = Nodes.nodes+Nodes.size;
        p->value = value;
        p->weight = weight;
        Nodes.size++;
        return p;
    }

    TreeNode *createTreeNode(Node *node){
        TreeNode *p = TreeNodes.nodes+TreeNodes.size;
        p->node = node;
        p->left = NULL;
        p->parent = NULL;
        p->right = NULL;
        TreeNodes.size++;
        return p;
    }

    Elem *createElem( TreeNode *node ){
        Elem *p = Elems.elems+Elems.size;
        p->tNode = node;
        Elems.size++;
        if( Elems.size == 200 ) Elems.size = 0// 注意不能超过200,否则数据错误
        return p; 
    }

    // 返回获取最小节点 
    Elem *removeMin(LinkList *list){
        Elem *elem = list->first;
        Elem *p = elem;
        int minWeight = p->tNode->node->weight ,w;
    //    while( elem->next != list->first ){
    //        elem = elem->next;
    //        w = elem->tNode->node->weight;
    //        if( minWeight > w ){
    //            p = elem;
    //            minWeight = w;
    //        }
    //    }
    //    removeElemL(list,p);
        int i;
        for ( i = 0 ; i < list->size ; i++ ){
            elem = elem->next;
            w = elem->tNode->node->weight;
            if(minWeight>w){
                p = elem;
                minWeight = w;
            }
        }
        removeElemL(list,p);
        return p;
    }

    char path[200] = {'0'}; // 字符串存储路径 
    void printBaseNodes(TreeNode *tNode, int layer){
        // 哈夫曼树是完全二叉树
        // 判断有无子节点即判断是否为编码节点
        if( tNode->left == NULL ){
            path[layer+1] = '';
            printf("字符:%c --> 路径: %s ",tNode->node->value,path);
        }
        else {
            path[layer+1] = '0';
            printBaseNodes(tNode->left,layer+1);
            path[layer+1] = '1';
            printBaseNodes(tNode->right,layer+1);
        }
    }

    void main(){
        init(); //全局变量初始化在这里 
        int i = 0;
        char c,hash[95];
        LinkList list;
        memset(hash,0,sizeof(hash)); //置零 
        while((c=getchar())!=' '){
            // 最好不要输入空格!!!
            hash[c-OFFSET]++;
        }
        printf("--------------------------------- ");
        initLinkList(&list); // 初始化链表 
        for(  i = 0 ; i < 95 ; i++ ){
            if( !hash[i] ) continue;
            printf("字符:%c --> 路径: %d ",i+OFFSET,hash[i]);
            Node *node = createNode(i+OFFSET,hash[i]);
            TreeNode *tNode = createTreeNode(node);
            Elem *elem = createElem(tNode);
            addFirst(&list,elem); // 完美 
        }
        // 走到这里已经完成了节点创建了
        // 现在要将节点构建成一棵哈夫曼二叉树
        printf("--------------------------------- ");

        if( !list.size ){
            printf("非法输入,即将退出程序! ");
            exit(1);
        }

        iflist.size == 1 ){
            // 如果小于1的话不需要构建树了
            printf("字符:%c --> 编码:%s ",list.first->tNode->node->value,"0"); 
            exit(0);
        }

        whilelist.size > 1 ){
            Elem *p1 = removeMin(&list);
            printf("最小权值(1)-字符:%c --> 权重:%d ",p1->tNode->node->value,p1->tNode->node->weight);
            Elem *p2 = removeMin(&list);
            printf("最小权值(2)-字符:%c --> 权重:%d ",p2->tNode->node->value,p2->tNode->node->weight);

            Node *p3Node = createNode(0,p1->tNode->node->weight + p2->tNode->node->weight); // 新建一个节点
            TreeNode *p3tNode = createTreeNode(p3Node); // 新建一个树节点
            Elem *p3 = createElem(p3tNode); // 新建链表元素

            p1->tNode->parent = p3->tNode; // 父节点指向p3
            p2->tNode->parent = p3->tNode; // 父节点指向p3

            p3->tNode->left = p1->tNode; // p1 权重最小,为左节点
            p3->tNode->right= p2->tNode; // p2 权重最大,为右节点

            // printf("权值:%d + %d --> %d ",p1->tNode->node->weight,p2->tNode->node->weight,p1->tNode->node->weight + p2->tNode->node->weight);

            printf("生成权值(3)-字符:%c --> 权重: %d ",p3->tNode->node->value,p3->tNode->node->weight);

            addFirst(&list,p3); //  添加到表头可以优先取出(如果权重最小的话)
        }
        printf("哈夫曼树构建完成 --> 表长:%d "list.size );
        // 如果表长不为 1 , 那么就是有问题了 
        // 到此已经构建好了哈夫曼树了 
        // 现在要写路径算法,输出编码 
        printf("--------------------------------- ");
        TreeNode *root = list.last->tNode; // 获取根节点,已经不需要链表了

        // 测试
        printf("根下左右叶 --> 左叶:%d --> 右叶:%d ",root->left->node->weight,root->right->node->weight); 

        printBaseNodes(root,0); 
    }

    运行结果

    ASDFAFDGVCKJFSDHAJDC
    ---------------------------------
    字符:A --> 路径: 3
    字符:C --> 路径: 2
    字符:D --> 路径: 4
    字符:F --> 路径: 3
    字符:G --> 路径: 1
    字符:H --> 路径: 1
    字符:J --> 路径: 2
    字符:K --> 路径: 1
    字符:S --> 路径: 2
    字符:V --> 路径: 1
    ---------------------------------
    最小权值(1)-字符:V --> 权重:1
    最小权值(2)-字符:K --> 权重:1
    生成权值(3)-字符:  --> 权重: 2
    最小权值(1)-字符:H --> 权重:1
    最小权值(2)-字符:G --> 权重:1
    生成权值(3)-字符:  --> 权重: 2
    最小权值(1)-字符:  --> 权重:2
    最小权值(2)-字符:  --> 权重:2
    生成权值(3)-字符:  --> 权重: 4
    最小权值(1)-字符:S --> 权重:2
    最小权值(2)-字符:J --> 权重:2
    生成权值(3)-字符:  --> 权重: 4
    最小权值(1)-字符:C --> 权重:2
    最小权值(2)-字符:F --> 权重:3
    生成权值(3)-字符:  --> 权重: 5
    最小权值(1)-字符:A --> 权重:3
    最小权值(2)-字符:  --> 权重:4
    生成权值(3)-字符:  --> 权重: 7
    最小权值(1)-字符:  --> 权重:4
    最小权值(2)-字符:D --> 权重:4
    生成权值(3)-字符:  --> 权重: 8
    最小权值(1)-字符:  --> 权重:5
    最小权值(2)-字符:  --> 权重:7
    生成权值(3)-字符:  --> 权重: 12
    最小权值(1)-字符:  --> 权重:8
    最小权值(2)-字符:  --> 权重:12
    生成权值(3)-字符:  --> 权重: 20
    哈夫曼树构建完成 --> 表长:1
    ---------------------------------
    根下左右叶 --> 左叶:8 --> 右叶:12
    字符:H --> 路径: 00000
    字符:G --> 路径: 00001
    字符:V --> 路径: 00010
    字符:K --> 路径: 00011
    字符:D --> 路径: 001
    字符:C --> 路径: 0100
    字符:F --> 路径: 0101
    字符:A --> 路径: 0110
    字符:S --> 路径: 01110
    字符:J --> 路径: 01111

    --------------------------------
    Process exited after 3.761 seconds with return value 24
    请按任意键继续. . .

  • 相关阅读:
    SYSBIOS学习笔记---线程(Threads)
    spring mvc: 生成RSS源
    spring mvc: Hibernate验证器(字段不能为空,在1-150自己)
    解决At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JAR
    spring mvc: 多解析器映射(资源绑定视图解析器 + 内部资源[普通模式/]视图解析器)
    spring mvc: 资源绑定视图解析器(不推荐)
    spring mvc:内部资源视图解析器2(注解实现)@Controller/@RequestMapping
    centos双机热备份
    spring mvc:内部资源视图解析器(注解实现)@Controller/@RequestMapping
    spring mvc: 可参数化的视图控制器(在配置中指定jsp文件)MultiActionController/SimpleUrlHandlerMapping/ParameterizableViewController
  • 原文地址:https://www.cnblogs.com/rcklos/p/12940150.html
Copyright © 2011-2022 走看看