STL实现哈夫曼算法

简介: 用C++ std::priority_queue 实现哈夫曼算法我想每个计算机专业的学生或多或少都接触过哈夫曼编码,数据结构中的老问题了。大体就是给出一些字符,和这些字符的出现频率,让你为这些字符设计一个二进制编码,要求频率最高的字符的编码最短。
用C++ std::priority_queue 实现哈夫曼算法我想每个计算机专业的学生或多或少都接触过哈夫曼编码,数据结构中的老问题了。大体就是给出一些字符,和这些字符的出现频率,让你为这些字符设计一个二进制编码,要求频率最高的字符的编码最短。解决的方法是构造一棵哈夫曼树(二叉树),其基本思路是,每次从这些字符中挑出两个频率最低的,然后构造一个新的结点,使新结点的左右孩子指针分别指向那两个节点。我想这个大家都很清楚了,我就不多说了。主要讲下这次我用C++实现时遇到的问题。首先,我定义了一个哈夫曼树结点: 

class hNode 

public: 
friend bool operator > (hNode n1,hNode n2); //定义了大于符号,供优先队列排列使用 
hNode(string d="",int i=0,hNode* l = NULL,hNode* r =NULL):left(l),right(r),data(d),value(i){} 
hNode* left; 
hNode* right; 
string data; //储存的字符串 
int value; //字符串出现的次数 
}; 

bool operator >(hNode n1,hNode n2) 

return n1.value > n2.value; 

因为只是算法课的小作业,所以我也不准备为hNode定义完整的二叉树操作,仅仅只是存放数据的对象,所以只有一个构造函数,并且所有的data member都是公有的。 

这此写这个算法会遇到大麻烦,主要因为是用了std::priority_queue容器。当时考虑到在哈夫曼中要每次挑选两个频率最小(即出现次数最小,我那个hNode里的value是出现的次数),很自然的就想到了std::priority_queue容器,优先队列每次都会弹出队列中权值最高的元素,这个特性无疑是实现哈夫曼算法的最佳选择。然而因为第一次用std::priority_queue容器,结果出了不少问题,好在最后都一一解决,也学到了不少东西。 

初步的设想是这样的,先把所有的hNode对象都压入优先队列中去,然后每次弹出两个,组成一个新的结点,再把新的结点压入队列,重复这一步骤,当队列中只有一个元素时,哈夫曼树也就完成了。像这样:(是错的,可别学) 

while(...) 

std::priority_queue<hNode> q; 
..... 
hNode h1 = q.top(); 
q.pop(); 
hNode h2 = q.top(); 
q.pop(); 
hNode r; 
r.left = h1; 
r.right = h2; 
r.value = h1.value + h2.value; 
q.push(r); 


然而遭遇的第一个问题是,STL的所有容器的的插入都是基于by value语义的,也就是要生成一个对象的副本放在容器中。这样的后果就是hNode的left,right指针都指到不知道什么地方去了。大家可以稍微画几个图试一下,就知道出了什么问题了。考虑一下后,发现如果队列里存放hNode的指针,就不会出现这个问题了,于是改写成: 


然而马上遭遇了第二个问题。std::priority_queue在判断优先关系的时候,直接比较指针的地址,而不是指针指向的对象的大小关系。而指针不是类,我没办法重写指针的比较操作。程序陷入了困境之中。std::priority_queue默认使用Greater<>模板来生成一个function object来对元素进行比较,我试图为Greater<>写一个hNode*的特化版本来改变优先队列对hNode*的比较,然而也没有成功。山重水复疑无路之时,突然想到为什么不直接为优先队列写一个function object来替代Greater<>不就可以了吗?赶快写下如下代码: 

struct phNodeComp 

bool operator () (const hNode*& left,const hNode*& right) const 

return left->value > right->value; 
}
 
}


然后把std::priority_queue的申明变为: 
priority_queue<hNode*,vector<hNode*>,phNodeComp > pq; 

终于把这个问题给解决了。看样子仅从书本上获得的知识是不牢靠的,一定要自己实践了才会有真正的认识。 

posted on 2004-03-19 19:47 Justin Shen 评论(6) 编辑 收藏 

Comments 
# re: 用C++ std::priority_queue 实现哈夫曼算法 
earthharp 
只有成树 
没有编码解码吗? 
我觉得用BucketSort还要优秀一些 
注意你的空间是否正确回收 
Posted @ 2004-03-20 01:06 
# re: 用C++ std::priority_queue 实现哈夫曼算法 
Justin Shen 
目前只做到把每个字符的编码打印出来,因为老师只要求到这点,实际做哈夫曼编码还是会有一点问题。空间回收在遍历哈夫曼树的过程中一起完成了。 

vector< int> v; 
void DoPrint(hNode* r) 

if (r->left ==NULL && r->right == NULL) 

cout << r->data <<": "; 
for (int i = 0;i<v.size();++i) 
cout << v; 
cout << endl; 
v.pop_back(); 
delete r; 
return ; 
}
 
if (r->left) 

v.push_back(0); 
DoPrint(r->left); 
}
 
if (r->right) 

v.push_back(1); 
DoPrint(r->right); 
}
 
v.pop_back(); 
}
 
目录
相关文章
|
12月前
|
算法 数据处理 C++
c++ STL划分算法;partition()、partition_copy()、stable_partition()、partition_point()详解
这些算法是C++ STL中处理和组织数据的强大工具,能够高效地实现复杂的数据处理逻辑。理解它们的差异和应用场景,将有助于编写更加高效和清晰的C++代码。
207 0
|
算法 前端开发 Linux
【常用技巧】C++ STL容器操作:6种常用场景算法
STL在Linux C++中使用的非常普遍,掌握并合适的使用各种容器至关重要!
234 10
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(5) 常用算术生成算法
黑马c++ STL常用算法 笔记(5) 常用算术生成算法
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(4) 常用拷贝和替换算法
黑马c++ STL常用算法 笔记(4) 常用拷贝和替换算法
|
算法 C++
STL算法大全
以上只是一部分STL算法的简单概述,每一个算法都有其特定的使用场景和规则,具体使用时需要参考相关文档或者教程进行深入理解和学习。
134 0
|
算法 搜索推荐 C++
C++之STL常用算法(遍历、查找、排序、拷贝、替换、算数生成、集合)
C++之STL常用算法(遍历、查找、排序、拷贝、替换、算数生成、集合)
133 0
|
算法 C++
【洛谷 P1090】[NOIP2004 提高组] 合并果子(贪心算法+哈夫曼编码+优先队列)
该编程题目要求设计算法,将不同种类的果子合并成一堆,使得消耗的体力最小。给定果子种类数`n`(1至10000)和每种果子的数量,需输出合并的最小体力值。使用优先队列(最小堆),每次取出两个数量最少的果子堆合并,并更新总体力消耗。样例输入为3种果子(1, 2, 9),输出最小体力耗费为15。提供的AC代码采用C++实现,通过优先队列优化合并顺序。
274 0
|
存储 算法 数据挖掘
【贪心算法经典应用】哈夫曼编码原理与算法详解 python
【贪心算法经典应用】哈夫曼编码原理与算法详解 python
|
算法 C++
c++算法学习笔记 (21) STL
c++算法学习笔记 (21) STL
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(6) 常用集合算法
黑马c++ STL常用算法 笔记(6) 常用集合算法

热门文章

最新文章