用c++实现哈夫曼编码

本文介绍了使用C++实现哈夫曼编码的过程,包括构建哈夫曼树、计算字符权重、生成编码等步骤,并分享了调试经验和技巧。

前面一个博客先行测试学习了怎么构造二叉树,怎么构造堆结构以及怎么实现优先权队列,这三个数据结构类型为使用c++实现哈夫曼编码打下了基础,哈夫曼树的构造在以上几个数据结构掌握之后应该没有大问题了,剩下的唯一一个实现哈夫曼编码的问题就是如何按照生成的哈夫曼树给值进行编码,用眼睛看的时候很简单,只要左右左右下去就能数出来,但是用程序实现就有带你难度了,为了解决这个问题我先进行了一个哈夫曼编码测试实验

1.计算权值补充实验

#include "iostream"
using namespace std;

struct code
{
    char code1;
    int num;
};




void main()
{
     code qz[100];

    for(int i=0;i<100;i++)
    {
        qz[i].num=0;
    }
    char *str=new char[100];
    gets(str);
    i=0;
    int j=0;
    bool adjust=0;
    while(str[i]!='\0')
    {
        for(j=0;qz[j].num!=0;j++)
        {
            if(str[i]==qz[j].code1)
            {
                adjust=1;
                break;
            }
        }

        if(adjust)
        {
            qz[j].num++;
        }
        else
        {
            qz[j].code1=str[i];
            qz[j].num++;
        }

        adjust=0;
        i++;
    }

    for(i=0;qz[i].num!=0;i++)
    {
        cout<<qz[i].code1<<' '<<qz[i].num<<endl;
    }
}

通过十进制的乘除算法得到类哈夫曼编码(root=1 左*2,右*2+1 叶子返回/2
非跟节点返回/2)再10进制转2进制(itoa(int,char,2))得到二进制形式的哈夫曼编码

2.哈夫曼编码源码

#include "BinaryTree.h"
#include "PrioQueue.h"


template <class T>
class HfmTree:public BinaryTree<T>
{
    private:
        T weight;
    public:
        operator T()const    //hfmtree对象隐式转换T类型时重载
        {
            return weight;
        }

        T getW()
        {
            return weight;
        }

        void putW(const T& x)
        {
            weight=x;
        }

        void SetNull()
        {
            root=NULL;
        }
        HfmTree<T> CreateHfmTree(T w[],int n);
        ~HfmTree<T>()
        {

        }
}; 

template <class T>
HfmTree<T> HfmTree<T>::CreateHfmTree(T w[],int n)
{
    PrioQueue <HfmTree<T> > pq(n);
    HfmTree<T> x,y,z,zero;
    for(int i=0;i<n;i++)      //将哈弗曼树压入最小优先权队列
    {
        z.MakeTree(w[i],x,y);
        z.putW(w[i]);
        pq.Append(z);      //每次压入都会排序
        z.SetNull();
    }
    for(i=1;i<n;i++)
    {
        pq.Serve(x);
        pq.Serve(y);     //取出最小的两个数
        z.MakeTree(x.getW()+y.getW(),x,y);
        z.putW(x.getW()+y.getW());
        pq.Append(z);
        z.SetNull();
    }
    pq.Serve(z);
    return z;
}

**通过本次试验二叉树的基本操作及哈夫曼编码译码系统的实现。不断完善代码和调试的过程中提升了编码能力和出错定位调试的速度。尤其是后者相比于第一次试验有了质的提升。
花时间最长的部分是哈夫曼编码部分,如何将构造好的哈弗曼树编码与输入的字符相对应和如何将人眼显而易见的哈夫曼编码用字符串的形式表示出来是最难的两个部分。
将各个模块均要直接访问的元素或者数组定义为static放在main函数外面。并在main函数开始的时候初始化。
Getcommand函数出了个小问题就是cin这段代码经常会被跳过。下断点调试过后定位到了问题所在。突然想到之前学习c的时候有说过回车键会被放入输入缓存中而导致下一次输入直接被跳过。查阅资料使用了cin.clear();//重置错误输入+cin.sync();//清空缓冲区解决了这个问题。
计算权值是一个较难部分使用了之前不怎么用的break跳出循环这个方法,加上自定义了一个结构体并定义了这个结构体的数组实现了哈夫曼字符和编码的储存。解决了前面提到的的哈弗曼树编码与输入的字符相对应这个难点。
从哈弗曼树解析出哈夫曼编码这个问题我借鉴了先序遍历的算法,通过判断左右子树是否同时为空的方式找到叶子节点。通过十进制的乘除算法得到类哈夫曼编码(root=1 左*2,右*2+1 叶子返回/2 非跟节点返回/2)再10进制转2进制(itoa(int,char,2))得到二进制形式的哈夫曼编码。
输入字符串使用char 字符串数组并cin方式输入
哈夫曼译码系统关键是匹配二进制字符串,使用memcpy逐个增加复制个数匹配字符。匹配完成后再利用memcpy消去匹配过了的二进制字符串并进行下一个字符的匹配。
这次main函数主要写了流程,具体的实现都放在了main函数外面: ShowMenu dectobin search showwithcode precodeshowHfmTree calcodesweight getqzi
getCommand HfmTree builduphfmtree这十个函数,简化了代码**

# 题目重述 请用 C++ 实现哈夫曼编码,用于数据压缩。要求构建哈夫曼树,并为每个字符生成对应的哈夫曼编码。 --- # 详解 哈夫曼编码是一种变长前缀编码,用于无损数据压缩。其核心思想是:**频率高的字符使用短编码,频率低的字符使用长编码**。 本实现步骤如下: 1. 定义哈夫曼树的节点结构; 2. 输入字符及其权重(频率); 3. 构建哈夫曼树:每次选出两个最小权重的节点合并; 4. 递归遍历哈夫曼树,生成每个字符的编码; 5. 输出每个字符对应的哈夫曼编码。 --- # 代码实现 ```cpp #include <iostream> #include <vector> #include <string> #include <algorithm> using namespace std; // 哈夫曼树节点 struct HTNode { int weight; // 权重(频率) int parent, lchild, rchild; HTNode() : weight(0), parent(-1), lchild(-1), rchild(-1) {} }; // 哈夫曼编码表 typedef vector<string> HuffmanCode; // 选择两个权值最小且未被选过的节点 void Select(const vector<HTNode>& HT, int n, int& s1, int& s2) { int min1 = INT_MAX, min2 = INT_MAX; s1 = s2 = -1; for (int i = 0; i < n; ++i) { if (HT[i].parent == -1 && HT[i].weight < min1) { min2 = min1; s2 = s1; min1 = HT[i].weight; s1 = i; } } for (int i = 0; i < n; ++i) { if (HT[i].parent == -1 && HT[i].weight < min2 && i != s1) { min2 = HT[i].weight; s2 = i; } } } // 构建哈夫曼树并生成编码 void HuffmanCoding(vector<int>& weights, HuffmanCode& huffmanCodes) { int n = weights.size(); if (n <= 1) return; int m = 2 * n - 1; // 总结点数 vector<HTNode> HT(m); // 初始化叶节点 for (int i = 0; i < n; ++i) { HT[i].weight = weights[i]; } // 构建哈夫曼树 for (int i = n; i < m; ++i) { int s1, s2; Select(HT, i, 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; } // 生成哈夫曼编码 huffmanCodes.resize(n); for (int i = 0; i < n; ++i) { string code = ""; for (int f = i, c = HT[f].parent; c != -1; f = c, c = HT[c].parent) { if (HT[c].lchild == f) { code += "0"; } else { code += "1"; } } reverse(code.begin(), code.end()); huffmanCodes[i] = code; } } // 主函数 int main() { int n; cout << "请输入字符数量: "; cin >> n; vector<int> weights(n); vector<char> chars(n); cout << "请输入每个字符及其频率:" << endl; for (int i = 0; i < n; ++i) { cout << "字符 " << i+1 << ": "; cin >> chars[i] >> weights[i]; } HuffmanCode codes; HuffmanCoding(weights, codes); cout << "\n哈夫曼编码结果:" << endl; for (int i = 0; i < n; ++i) { cout << chars[i] << ": " << codes[i] << endl; } return 0; } ``` --- # 运行示例 ``` 请输入字符数量: 4 请输入每个字符及其频率: 字符 1: A 5 字符 2: B 2 字符 3: C 9 字符 4: D 3 哈夫曼编码结果: A: 10 B: 110 C: 0 D: 111 ``` --- # 知识点 - **哈夫曼树构造原理**:依据贪心策略,每次合并两棵权值最小的子树,形成最优二叉树。 - **前缀编码性质**:任一编码不是另一编码的前缀,确保解码唯一性,避免歧义。 - **Select 函数设计**:从森林中选出两个无父节点且权值最小的节点,是建树关键步骤。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值