数据结构::如何实现哈夫曼树
2017-01-01 20:13
423 查看
【哈夫曼树的定义】:
哈夫曼树又称为最优二叉树,它是加权路径最短的二叉树。
不同于普通的二叉树,它的每个节点都有相应的权值,当我们构建的时候最终离根节点远的节点权重小,反之就比较大。
【哈夫曼树的实现】:
1、利用的核心思想:贪心算法
贪心算法:是指在问题进行求解的时候,总是做出当前看起来最好的选择。即就是说贪心算法做出不是整体的最优解,而是某种意义上的局部最优解。它不是对所有的问题都能得到整体最优解。
2、如何构建:
1)节点的设置:
我们了解哈夫曼树的定义后,我们知道就比普通的二叉树多了一个权重,因此,我们可以这样进行设计。(注意:我在这里给出了父节点可以不给出父节点,因为此时的构建两个孩子节点就足够了,我这里加上也就是为了稍稍的复习下三叉链)template<class T> struct HuffmanTreeNode { T _weight; HuffmanTreeNode<T>* _left; HuffmanTreeNode<T>* _right; HuffmanTreeNode<T>* _parent; HuffmanTreeNode(const T& x) :_left(NULL) ,_right(NULL) ,_parent(NULL) ,_weight(x) {} //此处的重载是后面的构建要用到,读者先不用看,到后面知道如何构建的时候,再回过头来串一遍 HuffmanTreeNode operator+(const HuffmanTreeNode& x) { return _weight+x._weight; } };
2)哈夫曼树构造函数的实现
//构造函数 HuffmanTree() :_root(NULL) {} HuffmanTree(T*a, size_t n,const T& invalue) { //因为在构建哈夫曼数的时候,比较的是两个权重的大小 //而权重的类型是Node*,所以要进行重载 struct com { bool operator()(Node* l,Node* r) { return l->_weight < r->_weight; } }; //利用堆来实现数据的存储 Heap<Node* ,com> hp; for(size_t i = 0; i<n; i++) { if(a[i] != invalue) hp.Push(new Node(a[i])); } //然后再进行构建哈夫曼树 //构建的最后,数组里会只剩下一个元素 while(hp.Size()>1) { //先取出堆顶的元素给左孩子 Node* left = hp.Top(); //出堆 hp.Pop(); //再取出堆顶的元素给右孩子 Node* right = hp.Top(); //出堆 hp.Pop(); //然后左右构建出一个父节点,即两者的权重之和 //(注意:此处需要进行重载+) Node* parent = new Node(left->_weight+right->_weight); //将节点要进行连接 parent->_left = left; parent->_right = right; //下面的这两条语句用于构建三叉链 right->_parent = parent; left->_parent = parent; //要将构建完后的节点扔回来,因为它可能不是最小的 hp.Push(parent); } //出循环后,数组中的存储就剩最后的一个节点,此节点就是根 _root = hp.Top(); }
【说明】:
*这里我写的很详细,我写了两个构造函数,一个是无参的,一个是通过数组来进行构建。
*我们在构建哈夫曼树的时候,是从数组中选取两个权重小的数,因此我们这里就会出现各种各样的办法
方法一:首先想到的就是排序,如果我们利用排序的话,每次取出数之后,前面我说到每次取出两个数进行处理后要将它们的父节点再扔回去,此时,当扔回去的时候,又要进行重新排序,所以利用数组的话,代价会很大
方法二:利用遍历,每次遍历找出最小的一个,重复,它的时间复杂度是O(n^2),效率比较低,所以不建议采取
方法三:利用堆采用这种方法的时间复杂度是O(nlg(n)),效率会比较高,所以这里我们采用这种方法
【说明】:此处我们要注意的一个点是,用堆的话,我们在堆里要用什么,怎么放的问题?
我们如果在堆里放节点的话就太大了(因为此处是结构体),应放节点的指针(来根据节点的权值 选择。):即就是这里的Heap<Node*>hp
*我这里是用我自己实现的堆来进行实现的,读者也可以用库里的STL 算法中的几个有关接口来进行实现
3)析构函数的实现
因为哈夫曼树也是二叉树,所以在析构的时候也要用到递归,和普通的二叉树的析构一样。//析构函数 ~HuffmanTree() { if(_root) Destory(_root); _root = NULL; } void Destory(Node* root) { if(root == NULL) return; Destory(root->_left); Destory(root->_right); delete root; }
【哈夫曼树的应用】:
1、哈夫曼编码:
关于哈夫曼的应用,在哈夫曼树构建好后,它的节点向左我们标记为0,向右我们标记为1,这样每个节点到根节点都会有自己的一系列关于0,1的序列,次序列就是哈夫曼编码。2、经典应用:文件压缩
此处我就先不展开了,具体的我在后面开一篇博客来详细的进行讲述,如何进行文件压缩相关文章推荐
- 数据结构(十二) 二叉树的基本操作 --- 创建一个二叉树 前中后序遍历二叉树
- 数据结构之线性表知识点小结(一)
- Zookeeper概念 & 数据结构
- 数据结构课程设计——烟大导航系统
- 数据结构课程设计-图书管理开发系统
- 数据结构----单链表(c++)
- 数据结构--学生成绩管理
- 数据结构课程设计——学生成绩管理系统
- Bloom Filter
- 数据结构实训之学生信息管理系统
- 数据结构课程设计
- 数据结构实训之学生信息管理系统
- 数据结构课程设计-ytu职工信息管理系统
- 数据结构与算法分析C++表述第二章编程题
- 数据结构课程设计--学生成绩管理系统
- binary indexed tree
- redis集群实现(七)sentinel数据结构和初始
- 数据结构——中国邮递员问题
- java数据结构与算法之平衡二叉树(AVL树)的设计与实现
- 【瞎搞】回文自动机