网络编程 | 站长之家 | 网页制作 | 图形图象 | 操作系统 | 冲浪宝典 | 软件教学 | 网络办公 | 邮件系统 | 网络安全 | 认证考试 | 系统进程
Firefox | IE | Maxthon | 迅雷 | 电驴 | BitComet | FlashGet | QQ | QQ空间 | Vista | 输入法 | Ghost | Word | Excel | wps | Powerpoint
asp | .net | php | jsp | Sql | c# | Ajax | xml | Dreamweaver | FrontPages | Javascript | css | photoshop | fireworks | Flash | Cad | Discuz!
当前位置 > 网站建设学院 > 网络编程 > C/C++
Tag:注入,存储过程,分页,安全,优化,xmlhttp,fso,jmail,application,session,防盗链,stream,无组件,组件,md5,乱码,缓存,加密,验证码,算法,cookies,ubb,正则表达式,水印,索引,日志,压缩,base64,url重写,上传,控件,Web.config,JDBC,函数,内存,PDF,迁移,结构,破解,编译,配置,进程,分词,IIS,Apache,Tomcat,phpmyadmin,Gzip,触发器,socket
网络编程:ASP教程,ASP.NET教程,PHP教程,JSP教程,C#教程,数据库,XML教程,Ajax,Java,Perl,Shell,VB教程,Delphi,C/C++教程,软件工程,J2EE/J2ME,移动开发
本月文章推荐
.数据结构学习(C++)之栈和队列.
.【用计算机求解经典问题】难忘的.
.C语言库函数(G类字母).
.C++版权和版本的声明.
.C++中的引用(reference).
.C++程序设计例解(04).
.C++习题与解析-类和对象.
.C++Builder中"异形"按.
.Turbo C作图一例.
.新手入门:C++中的函数重载.
.使用C++异常来取代exit()函数.
.在C++语言中,关于内联函数(inlin.
.新手入门:C++中堆内存(heap)的概.
.链表的C语言实现之动态内存分配.
.使用Rational进行C++转换的技巧.
.为Windows窗口标题栏添加新按钮.
.偶写的链表、堆栈、队列的集合操.
.用HOOK函数自动关闭IE广告窗口.
.用拷贝钩子实现对文件夹的监控.
.文件加锁的例题示范.

利用C++实现哈夫曼算法

发表日期:2008-3-8



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

hNode* makeTree(priority_queue<hNode*> pq)
{
 hNode* p1 = NULL;
 hNode* p2 = NULL;
 hNode* r = NULL;
 while( !pq.empty())
 {
  p1 = pq.top();
  pq.pop();
  if (pq.empty())
  {
   r = p1;
   return r;
  }
  p2 = pq.top();
  pq.pop();
  r =new hNode;
  r->left = p1;
  r->right = p2;
  r->value = p1->value +p2->value;
  pq.push(r);
 }
 return NULL;
}
  然而马上遭遇了第二个问题。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;
  终于把这个问题给解决了。
看样子仅从书本上获得的知识是不牢靠的,一定要自己实践了才会有真正的熟悉。 更多文章 更多内容请看C/C++技术专题专题,或
上一篇:3. 关键字和标识符 人气:546
下一篇:C++箴言:防止异常离开析构函数 人气:702
浏览全部C/C++的内容 Dreamweaver插件下载 网页广告代码 祝你圣诞节快乐 2009年新年快乐