huffman(赫夫曼编码)之C/C++实现

有点儿兴奋,有点儿紧张,这是我人生发的第一篇正式的blog,在进入正文之前,请容许我说一点儿序言,之所以发这一blog文章,一来是想记录下自己一天做了些什么,学了些什么,所谓:好记性不如烂笔头,以前没有体会到这一点儿的好,在经过一个项目的总结之后,看到同事在做一个项目的总结文档和自己的一比,才知道,原来实时记录自己所学,所想的重要性,成功是一点儿一点儿积累起来的;二来,也是便于自己经常的复习所学的东西,不致于要用的时候,到处找,像无头苍蝇一般;

好了,让我们进入正题吧,今天所学的是用代码实现“赫夫曼编码”。当然,什么是赫夫曼编码呢?在此,我就不多说了,因为本人也不是很了解,只知道“赫夫曼编码”又称“前缀编码”,在《数据结构(C语言)版——严蔚敏感编著》中第146页有些许的介绍。有兴趣的朋友可以下去多了解学习一下,本人只注重代码的实现。谢谢!

让我先来看看生成“赫夫曼编码”所需的数据结构:

typedef struct_huffman_node

{

unsigned int weight;

unsigned int parent,lchild,rchild;

_huffman_node(const int w,const int p,const int l ,const int r)

{

weight=w;parent=p;lchild=l;rchild=r;

}

bool operator<(const _huffman_node t)

{

return weight<t.weight;

}

}HTNode,*HuffmanTree;

typedef char ** HuffmanCode;//动态分配数组存储赫夫曼编码表

注:之所以把父指针、左子树、右子树定义为unsigned int是因为我采用的是数据存储结构是数组,故,parentlchildrchild存放的是相应的下标值,无需用指针来存储。当然,网上也有很多利用其二叉树来实现的方法,由于自己还没有达到那个高度,所以只好先用数组来实现了,慢慢来,慢慢提高,慢慢的接近大神。

在这个结构体中,还定义了一个结构体的构造函数,与“<”运算符的函数。这样便于对结构体赋值及比较。稍后,为大家带来详细的讲解。

其次,来看实现编码的函数。

void HuffmanCoding( HuffmanTree &HT,HuffmanCode& HC,int *w,int n )

{

if (w==NULL || n<=1)

{

return;

}

if (HT!=NULL || HC !=NULL)

{

throw INVALID_PARAMETER;

return;

}

int m=2*n-1;

HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//从下标开始计数

if (HT == NULL)

{

return;//是否还有什么工作没有做

}

HuffmanTree p = HT;

*p=HTNode(0,0,0,0);

p++;

for (int i =1; i<=n; ++i,++p,++w)

{

*p=HTNode(*w,0,0,0);//利用结点的权值,初始化huffman树的前n个结点。这就是用了结构体的构造函数好处,可以少写很多的赋值代码。

}

for(int i =n+1;i<=m;i++,++p)

{

*p=HTNode(0,0,0,0);//初始化huffman树中的双亲节点。

}

unsigned int s1=0,s2=0;

for(int i=n+1;i<=m;++i)

{

//建立赫夫曼树

SelectSubtree(HT,i-1,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;

}

//

/*

*从叶子到根逆向求每个字符的赫夫曼编码

*/

HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量

char * cd =(char*)malloc(n*sizeof(char));

if (HC==NULL || cd ==NULL)

{

return ;//是否还有什么别的工作没有做。

}

cd[n-1]='\0';

int start=0;

int c =0 ,f =0,len=0;

for (int i = 1;i<=n;++i)

{

//逐个字符求赫夫曼编码

start = n-1;

for (c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)

{

if (HT[f].lchild==c)

{

cd[--start]='0';

}

else

{

cd[--start]='1';

}

}

len = n-start;

HC[i] = (char *)malloc((len+1)*sizeof(char));//为第i个字符编码分配空间

memset(HC[i],0,len+1);

strcpy_s(HC[i],len,&cd[start]);

}

free(cd);

}

注:1、要注意函数中,多处用了malloc动态的开辟内存空间,要及时的对新开辟的内存空间初始化。

2、要注意对开辟空间数据的操作,本人在对此指针操作的时候,多次造成指针越界,调试非常的痛苦,请朋友们一定要注意啦。如:strcpy_s函数,开始由于没有对cd开辟的空间做初始化为0的操作,造成了,函数多次弹出“buffer too small”的bug.

第三,HuffmanCoding函数中调用了一个SelectSubtree(...)函数,当然函数的定义如下,

void SelectSubtree( const HuffmanTree &HT,int limit,unsigned int & s1,unsigned int& s2 )

{

if (HT == NULL || limit<=0)

{

throw INVALID_PARAMETER;//参数异常

}

unsigned int min_1 = 0,min_2 =0;

for (int i = 1;i<=limit;i++)

{

if (HT[i].parent!=0)

{

continue;

}

else if (min_1==0)

{

min_1=i;

}

else if (min_2 == 0)

{

if (HT[i]<HT[min_1])

{//这里就利用了结构体中”<”运算符的作用,是不是看得很直观啊!

min_2=min_1;

min_1=i;

}

else

{

min_2=i;

}

}

else if (HT[i]<HT[min_1])

{

min_2=min_1;

min_1=i;

}

else if (HT[i]<HT[min_2])

{

min_2 = i;

}

}

s1=min_1;

s2=min_2;

}

好了,到此,今天的blog已接近尾声了,希望能帮助到难看的游客朋友,如果,你发现了其中的不足,请您提出您的的宝贵建议,让我们共同提高,共同进步,本人的QQ1272725,当然,要是你还有什么不明白的地方,也可以加本人的QQ联系,或者留言,一起讨论。谢谢……

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈夫曼树是一种用于数据压缩的树形结构,可以通过构建哈夫曼树来得到哈夫曼编码。下面是用C++实现哈夫曼树和哈夫曼编码的示例代码: ```c++ #include <iostream> #include <queue> #include <vector> #include <string> #include <unordered_map> using namespace std; // 哈夫曼树节点 struct HuffmanNode { char ch; // 字符 int freq; // 出现频率 HuffmanNode *left, *right; // 左右子节点 HuffmanNode(char c, int f) : ch(c), freq(f), left(nullptr), right(nullptr) {} }; // 用于比较哈夫曼树节点的函数对象 class HuffmanNodeCompare { public: bool operator()(HuffmanNode* a, HuffmanNode* b) { return a->freq > b->freq; // 以出现频率为优先级 } }; // 构建哈夫曼树 HuffmanNode* buildHuffmanTree(string str) { unordered_map<char, int> freqMap; // 统计各字符出现频率 for (char c : str) { freqMap[c]++; } priority_queue<HuffmanNode*, vector<HuffmanNode*>, HuffmanNodeCompare> pq; // 将字符及其出现频率构建成哈夫曼树节点,并加入优先队列 for (auto it = freqMap.begin(); it != freqMap.end(); it++) { HuffmanNode* node = new HuffmanNode(it->first, it->second); pq.push(node); } // 逐步合并哈夫曼树节点,直到只剩下一个节点,即根节点 while (pq.size() > 1) { HuffmanNode* left = pq.top(); pq.pop(); HuffmanNode* right = pq.top(); pq.pop(); HuffmanNode* parent = new HuffmanNode('$', left->freq + right->freq); parent->left = left; parent->right = right; pq.push(parent); } return pq.top(); } // 递归遍历哈夫曼树,得到字符的哈夫曼编码 void getHuffmanCode(HuffmanNode* node, string code, unordered_map<char, string>& codeMap) { if (!node) { return; } if (node->ch != '$') { codeMap[node->ch] = code; } getHuffmanCode(node->left, code + "0", codeMap); getHuffmanCode(node->right, code + "1", codeMap); } int main() { string str = "hello world"; HuffmanNode* root = buildHuffmanTree(str); unordered_map<char, string> codeMap; getHuffmanCode(root, "", codeMap); cout << "Huffman codes:" << endl; for (auto it : codeMap) { cout << it.first << ": " << it.second << endl; } return 0; } ``` 代码中,先统计字符串中各字符的出现频率,然后将它们构建成哈夫曼树节点,并加入优先队列。接着,逐步合并哈夫曼树节点,直到只剩下一个节点,即根节点。最后,递归遍历哈夫曼树,得到各字符的哈夫曼编码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值