• 2013=12=2 哈夫曼树


    //哈夫曼编码算法 2009-06-05 13:10:22 
    //分类: C/C++
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <float.h>
    #include <memory.h>
    #include "string.h"
    
    #ifndef __HUFFMAN_H_FE
    #define __HUFFMAN_H_FE
    // 哈夫曼树的节点结构
    typedef struct _huff_node {
        float      weight;            // 节点权重
        int        lchild;            // 节点的左孩子
        int        rchild;            // 节点的右孩子
        int        parent;            // 节点的父节点(方便沿路径输出编码)
    } HUFF_NODE, *PHUFF_NODE;
    
    // 哈夫曼树
    typedef struct _huff_tree {
        int            leaf_num;        // 树中叶节点也就是需要编码的节点的数目
        int            node_num;        // 对应于叶节点的数目,哈夫曼树中一共需要 2*leaf_num - 1 个节点
        PHUFF_NODE    nodes;            // 树中所有的节点,用一个数组存放这些节点
        char**        huff_code;        // 叶节点对应的哈夫曼编码(用二维数组来存放编码)
    } HUFF_TREE;
    
    
    // 初始化一棵哈夫曼树,包括为这棵树分配空间以及权重等的初始化
    void init_huff_tree(float *weights, int leaf_num, HUFF_TREE &tree);
    
    // 创建一棵哈夫曼树,前提是必须已经初始化了
    void create_huff_tree(HUFF_TREE &tree);
    
    // 打印一棵已经创建好的哈夫曼树的叶节点对应的哈夫曼编码
    void print_huff_code(HUFF_TREE &tree);
    
    // 销毁一棵哈夫曼树,释放初始化时申请的节点以及编码空间
    void destroy_huff_tree(HUFF_TREE &tree);
    #endif
    
    //哈夫曼树的编码
    void huff_encode(HUFF_TREE &tree);
    
    // 初始化一棵哈夫曼树
    void init_huff_tree(float *weights, int leaf_num, HUFF_TREE &tree)
    {
        // 保存叶节点数
        tree.leaf_num = leaf_num;
        // 根据叶节点数计算整棵哈夫曼树需要的节点数
        // 一棵二叉树中,出度为0、1、2的节点之间有如下关系:
        // n = n0 + n1 + n2 且 n = n1 + 2*n2 + 1
        // 因此,有 n2 = n0 - 1
        // 在哈夫曼树中,只有出度为0和2的节点
        // 因此,n = n0 + n2 = 2*n0 - 1
        tree.node_num = 2 * leaf_num - 1;
        // 为节点分配空间
        tree.nodes = new HUFF_NODE[tree.node_num];
        if (tree.nodes == NULL) {
            printf("memry out. ");
            exit(1);
        }
        // 为每个叶节点分配存储哈夫曼编码的字符数组
        tree.huff_code = new char*[leaf_num];
        if (tree.huff_code == NULL) {
            printf("memory out. ");
            exit(1);
        }
        else {
            for (int i = 0; i < leaf_num; i++) {
                if ((tree.huff_code[i] = new char[leaf_num + 1]) == NULL) {
                    printf("memory out. ");
                    exit(1);
                }
                else {
                    memset(tree.huff_code[i], 0, leaf_num + 1);
                }
            }
        }
        // 对节点值进行初始化
        for (int i = 0; i < tree.node_num; i++) {
            // 叶节点存储在节点数组前部,且赋上它们的初始权值
            if (i < leaf_num) {
                tree.nodes[i].weight = weights[i];
            }
            else {
                tree.nodes[i].weight = 0;
            }
            // 左右孩子以及父亲节点指针(数组索引)初始化为空(-1)
            tree.nodes[i].parent = -1;
            tree.nodes[i].lchild = -1;
            tree.nodes[i].rchild = -1;
        }
    }
    
    
    // 根据初始化之后的哈夫曼树创建整棵完整的哈夫曼树,包括每个叶节点的哈夫曼编码
    void create_huff_tree(HUFF_TREE &tree)
    {
        float min1 = FLT_MAX, min2 = FLT_MAX;        // min1存放当前最小值,min2存放当前次小值
        int lchild_temp = -1, rchild_temp = -1;
        // 组织二叉树的结构
        // 每一轮从那些还没有父节点的节点中选出权值最小的两个节点
        // 节点数组中的下一个节点的左右孩子指针分别指向最小和次小的两个节点
        // 并且,权值置为以上两个节点的和
        // 最小节点和次小节点的父节点指针置为该节点的索引
        for (int i = tree.leaf_num; i < tree.node_num; i++) {
            // 从那些还没有父节点的数组中选择权值最小的两个节点
            for (int j = 0; j < i; j++) {
                if (tree.nodes[j].parent == -1) {
                    if (tree.nodes[j].weight < min1) {
                        min2 = min1;
                        rchild_temp = lchild_temp;
                        min1 = tree.nodes[j].weight;
                        lchild_temp = j;
                    }
                    else if (tree.nodes[j].weight < min2) {
                        min2 = tree.nodes[j].weight;
                        rchild_temp = j;
                    }
                }
            }
            tree.nodes[lchild_temp].parent = i;
            tree.nodes[rchild_temp].parent = i;
            tree.nodes[i].lchild = lchild_temp;
            tree.nodes[i].rchild = rchild_temp;
            tree.nodes[i].weight = min1 + min2;
            min1 = FLT_MAX;
            min2 = FLT_MAX;
            lchild_temp = -1;
            rchild_temp = -1;
        }
        // 对哈夫曼树的叶节点进行编码
        huff_encode(tree);
    }
    // 对哈夫曼树的叶节点进行编码
    void huff_encode(HUFF_TREE &tree)
    {
        // 一个临时数组,从后向前存储哈夫曼编码
        char* code_temp = new char[tree.leaf_num + 1];
        memset(code_temp, 0, tree.leaf_num + 1);
        // 写指针初始位于临时数组末尾
        int start_pos = tree.leaf_num;
        // 从叶节点开始,一直向上直到根节点位置
        // 为每一个叶节点进行编码
        for (int i = 0; i < tree.leaf_num; i++) {
            int cur_node = i;
            int parent = tree.nodes[i].parent;
            // 一直到根节点
            while (parent != -1) {
                // 若当前节点是父节点的左孩子,则编码为0
                if (tree.nodes[parent].lchild == cur_node) {
                    code_temp[--start_pos] = '0';
                }
                // 若当前节点是父节点的右孩子,则编码为1
                else if (tree.nodes[parent].rchild == cur_node) {
                    code_temp[--start_pos] = '1';
                }
                // 继续向上
                cur_node = parent;
                parent = tree.nodes[parent].parent;
            }
            // 将临时编码数组中的编码拷贝到哈夫曼树中的节点对应的编码存储空间中
            memcpy(tree.huff_code[i], (code_temp + start_pos), (tree.leaf_num - start_pos + 1));
            memset(code_temp, 0, tree.leaf_num + 1);
            start_pos = tree.leaf_num;
        }
        delete[] code_temp;
    }
    
    
    
    // 打印每个叶节点的哈夫曼编码
    void print_huff_code(HUFF_TREE &tree)
    {
    	int wpl=0;
        for (int i = 0; i < tree.leaf_num; i++) {
            printf("
     node weight: %.2f", tree.nodes[i].weight);
            printf(" huffman code: %s ", tree.huff_code[i]);
            wpl+=tree.nodes[i].weight*strlen(tree.huff_code[i]);
        }
    	printf("
     huffman WPL=%d
     ", wpl);
    }
    
    
    // 销毁哈夫曼树
    void destroy_huff_tree(HUFF_TREE &tree)
    {
        // 释放节点空间
        if (tree.nodes != NULL) {
            delete[] tree.nodes;
        }
        // 释放哈夫曼编码空间
        if (tree.huff_code != NULL) {
            for (int i = 0; i < tree.leaf_num; i++) {
                if (tree.huff_code[i] != NULL) {
                    delete[] tree.huff_code[i];
                }
            }
            delete[] tree.huff_code;
        }
    }
    
    int main(void)
    {
        float weights[10] = { 14, 3, 7, 9, 34, 8, 12, 1, 5, 20 };
        HUFF_TREE huff_tree;
        printf("weights of the nodes are: ");
        for (int i = 0; i < 10; i++) {
            printf("%.2f  ", weights[i]);
        }
        printf(" ");
        init_huff_tree(weights, 10, huff_tree);
        create_huff_tree(huff_tree);
        print_huff_code(huff_tree);
        destroy_huff_tree(huff_tree);
        system("pause");
        return 0;
    }
    
  • 相关阅读:
    RPC细谈
    RPC浅谈
    动态规划
    libco 的定时器实现: 时间轮
    一次HTTP请求的完整过程——协议篇(DNS、TCP、HTTP)
    多个CPU、多核CPU以及超线程(Hyper-Threading)
    Linux下which、whereis、locate、find命令的区别
    warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
    使用OutputDebugString输出调试信息
    VS或windows用代码产生GUID
  • 原文地址:https://www.cnblogs.com/wc1903036673/p/3453558.html
Copyright © 2020-2023  润新知