博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
哈夫曼树
阅读量:4603 次
发布时间:2019-06-09

本文共 9277 字,大约阅读时间需要 30 分钟。

一、定义

哈夫曼树,又称最优树,是一类带权路径长度最短的树。首先有几个概念需要清楚:

1、路径和路径长度

从树中一个结点到另一个结点之间的分支构成两个结点的路径,路径上的分支数目叫做路径长度。树的路径长度是从树根到每一个结点的路径长度之和。

2、带权路径长度

结点的带权路径长度为从该结点到树根之间的路径长度与结点上权的乘积。树的带权路径长度为树中所有叶子结点的带权路径长度之和,通常记作WPL。

若有n个权值为w1,w2,...,wn的结点构成一棵有n个叶子结点的二叉树,则树的带权路径最小的二叉树叫做哈夫曼树或最优二叉树。

 

在上图中,3棵二叉树都有4个叶子结点a、b、c、d,分别带权7、5、2、4,则它们的带权路径长度为

(a)WPL = 7*2 + 5*2 + 2*2 + 4*2 = 36

(b)WPL = 4*2 + 7*3 + 5*3 + 2*1 = 46

(c)WPL = 7*1 + 5*2 + 2*3 + 4*3 = 35

其中(c)的WPL最小,可以验证,(c)恰为哈夫曼树。

二、哈夫曼树操作的C++实现

1、结点表示

class Node{public:    int val;        //结点的值    Node* left;     //结点左孩子    Node* right;    //结点右孩子    Node* parent;   //结点的父结点    Node(int val) :val(val), left(NULL), right(NULL), parent(NULL) {}    Node(int val, Node* left, Node* right, Node* parent) :val(val), left(left), right(right), parent(parent) {}};

2、操作

class HuffmanTree{private:    priority_queue
, Compare> nodes; //优先队列(小根堆) Node* root;public: HuffmanTree(); ~HuffmanTree(); /*创建哈夫曼树,返回根结点*/ Node* create(); /*先序遍历*/ void preOrder(Node* root); /*中序遍历*/ void inOrder(Node* root); /*后序遍历*/ void postOrder(Node* root); /*销毁哈夫曼树*/ void destroyTree(Node* root);};

3、创建哈夫曼树

假设有n个结点,n个结点的权值分别为w1,w2,...,wn,构成的二叉树的集合为F={T1,T2,...,Tn},则可构造一棵含有n个叶子结点的哈夫曼树。步骤如下:

(1)从F中选取两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,其新的二叉树的权值为其左右子树根结点权值之和;

(2)从F中删除上一步选取的两棵二叉树,将新构造的树放到F中;

(3)重复(1)(2),直到F只含一棵树为止。

从上面的步骤可以看出,每次选取n个结点中权值最小的两个结点,可以使用小根堆来实现,这里为了简单起见,使用STL中的priority_queue来实现选取权值最小结点的操作。为了保持队列中结点一直按从小到大排序,需要自定义比较函数。由于优先队列中的数据类型为指针类型,所以不能通过重载运算符<来实现,而是要写一个比较类,在构造优先队列时,传入其第3个参数(完整代码中的类Compare)。

4、遍历与删除哈夫曼树

遍历与删除哈夫曼树与遍历和删除普通二叉树没有大的区别,这里只写出代码。

5、哈夫曼树完整代码

1 #include 
2 #include
3 using namespace std; 4 5 class Node 6 { 7 public: 8 int val; //结点的值 9 Node* left; //结点左孩子 10 Node* right; //结点右孩子 11 Node* parent; //结点的父结点 12 13 Node(int val) :val(val), left(NULL), right(NULL), parent(NULL) {} 14 Node(int val, Node* left, Node* right, Node* parent) :val(val), left(left), right(right), parent(parent) {} 15 }; 16 17 class Compare //比较类,用于构造Node*类型的priority_queue 18 { 19 public: 20 bool operator() (Node* a, Node* b) 21 { 22 return a->val > b->val; //结点的值越小越靠前 23 } 24 }; 25 26 class HuffmanTree 27 { 28 private: 29 priority_queue
, Compare> nodes; //优先队列(小根堆) 30 Node* root; 31 public: 32 HuffmanTree(); 33 ~HuffmanTree(); 34 35 /*创建哈夫曼树,返回根结点*/ 36 Node* create(); 37 38 /*先序遍历*/ 39 void preOrder(Node* root); 40 41 /*中序遍历*/ 42 void inOrder(Node* root); 43 44 /*后序遍历*/ 45 void postOrder(Node* root); 46 47 /*销毁哈夫曼树*/ 48 void destroyTree(Node* root); 49 }; 50 51 HuffmanTree::HuffmanTree() 52 { 53 while (!nodes.empty()) nodes.pop(); 54 int a[] = { 4,3,5,1,2 }; 55 int len = sizeof(a) / sizeof(a[0]); 56 for (int i = 0;i < len; i++) //使用数组的值初始化结点 57 nodes.push(new Node(a[i])); 58 59 root = NULL; 60 } 61 62 HuffmanTree::~HuffmanTree() 63 { 64 destroyTree(root); 65 } 66 67 Node* HuffmanTree::create() 68 { 69 while (nodes.size() > 1) 70 { 71 Node* node1 = nodes.top(); //选取结点值最小的两个结点 72 nodes.pop(); 73 Node* node2 = nodes.top(); 74 nodes.pop(); 75 76 Node* root = new Node(node1->val + node2->val); //新结点的值为两个结点值之和 77 root->left = node1; //将新结点设为两结点的根结点 78 root->right = node2; 79 node1->parent = root; 80 node2->parent = root; 81 nodes.push(root); 82 } 83 84 root = nodes.top(); //队列中最后一个结点即为哈夫曼树的根结点 85 nodes.pop(); 86 87 return root; 88 } 89 90 /*先序遍历*/ 91 void HuffmanTree::preOrder(Node* root) 92 { 93 if (root == NULL) 94 return; 95 cout << root->val << " "; 96 preOrder(root->left); 97 preOrder(root->right); 98 } 99 100 /*中序遍历*/101 void HuffmanTree::inOrder(Node* root)102 {103 if (root == NULL)104 return;105 inOrder(root->left);106 cout << root->val << " ";107 inOrder(root->right);108 }109 110 /*后序遍历*/111 void HuffmanTree::postOrder(Node* root)112 {113 if (root == NULL)114 return;115 postOrder(root->left);116 postOrder(root->right);117 cout << root->val << " ";118 }119 120 /*销毁哈夫曼树*/121 void HuffmanTree::destroyTree(Node* root)122 {123 if (root == NULL)124 return;125 destroyTree(root->left);126 destroyTree(root->right);127 delete root;128 }129 130 int main()131 {132 /*创建哈夫曼树*/133 HuffmanTree* huffmanTree = new HuffmanTree();134 Node* root = huffmanTree->create();135 136 cout << "先序遍历:";137 huffmanTree->preOrder(root);138 cout << endl;139 140 cout << "中序遍历:";141 huffmanTree->inOrder(root);142 cout << endl;143 144 cout << "后序遍历:";145 huffmanTree->postOrder(root);146 cout << endl;147 148 return 0;149 }

结果:

先序遍历:15 6 3 3 1 2 9 4 5 中序遍历:3 6 1 3 2 15 4 9 5 后序遍历:3 1 2 3 6 4 5 9 15

 三、哈夫曼编码

我们约定左分支表示字符'0',右分支表示字符'1',在哈夫曼树中从根结点开始,到叶子结点的路径上分支字符组成的字符串为该叶子结点的哈夫曼编码。上面代码所创建的哈夫曼树如下所示:

可以看出3被编码为00,1为010,2为011,4为10,5为11。在这些编码中,任何一个字符的编码均不是另一个字符编码的前缀。

实现代码:

/*哈夫曼编码*/void HuffmanTree::encode(Node* root, string code){    if (root->left == NULL&&root->right == NULL)    //叶子结点        cout << root->val << " 被编码为 " << code << endl;    if (root->left != NULL)    {        code += "0";                //左子树,编码code添加'0'        encode(root->left, code);        code.erase(code.end()-1);   //删除上一步添加的'0'    }    if (root->right != NULL)    {        code += "1";                //右子树,编码code添加'1'        encode(root->right, code);        code.erase(code.end()-1);   //删除上一步添加的'1'    }}

完整代码:

#include 
#include
#include
#include
using namespace std;class Node{public: int val; //结点的值 Node* left; //结点左孩子 Node* right; //结点右孩子 Node* parent; //结点的父结点 Node(int val) :val(val), left(NULL), right(NULL), parent(NULL) {} Node(int val, Node* left, Node* right, Node* parent) :val(val), left(left), right(right), parent(parent) {}};class Compare //比较类,用于构造Node*类型的priority_queue{public: bool operator() (Node* a, Node* b) { return a->val > b->val; //结点的值越小越靠前 }};class HuffmanTree{private: priority_queue
, Compare> nodes; //优先队列(小根堆) Node* root;public: HuffmanTree(); ~HuffmanTree(); /*创建哈夫曼树,返回根结点*/ Node* create(); /*哈夫曼编码*/ void encode(Node* node, string code); /*先序遍历*/ void preOrder(Node* root); /*中序遍历*/ void inOrder(Node* root); /*后序遍历*/ void postOrder(Node* root); /*销毁哈夫曼树*/ void destroyTree(Node* root);};HuffmanTree::HuffmanTree(){ while (!nodes.empty()) nodes.pop(); int a[] = { 4,3,5,1,2 }; int len = sizeof(a) / sizeof(a[0]); for (int i = 0;i < len; i++) //使用数组的值初始化结点 nodes.push(new Node(a[i])); root = NULL;}HuffmanTree::~HuffmanTree(){ destroyTree(root);}Node* HuffmanTree::create(){ while (nodes.size() > 1) { Node* node1 = nodes.top(); //选取结点值最小的两个结点 nodes.pop(); Node* node2 = nodes.top(); nodes.pop(); Node* root = new Node(node1->val + node2->val); //新结点的值为两个结点值之和 root->left = node1; //将新结点设为两结点的根结点 root->right = node2; node1->parent = root; node2->parent = root; nodes.push(root); } root = nodes.top(); //队列中最后一个结点即为哈夫曼树的根结点 nodes.pop(); return root;}/*哈夫曼编码*/void HuffmanTree::encode(Node* root, string code){ if (root->left == NULL&&root->right == NULL) //叶子结点 cout << root->val << " 被编码为 " << code << endl; if (root->left != NULL) { code += "0"; //左子树,编码code添加'0' encode(root->left, code); code.erase(code.end()-1); //删除上一步添加的'0' } if (root->right != NULL) { code += "1"; //右子树,编码code添加'1' encode(root->right, code); code.erase(code.end()-1); //删除上一步添加的'1' }}/*先序遍历*/void HuffmanTree::preOrder(Node* root){ if (root == NULL) return; cout << root->val << " "; preOrder(root->left); preOrder(root->right);}/*中序遍历*/void HuffmanTree::inOrder(Node* root){ if (root == NULL) return; inOrder(root->left); cout << root->val << " "; inOrder(root->right);}/*后序遍历*/void HuffmanTree::postOrder(Node* root){ if (root == NULL) return; postOrder(root->left); postOrder(root->right); cout << root->val << " ";}/*销毁哈夫曼树*/void HuffmanTree::destroyTree(Node* root){ if (root == NULL) return; destroyTree(root->left); destroyTree(root->right); delete root;}int main(){ /*创建哈夫曼树*/ HuffmanTree* huffmanTree = new HuffmanTree(); Node* root = huffmanTree->create(); cout << "先序遍历:"; huffmanTree->preOrder(root); cout << endl; cout << "中序遍历:"; huffmanTree->inOrder(root); cout << endl; cout << "后序遍历:"; huffmanTree->postOrder(root); cout << endl; cout << "哈夫曼编码:" << endl; huffmanTree->encode(root, ""); return 0;}

结果:

先序遍历:15 6 3 3 1 2 9 4 5 中序遍历:3 6 1 3 2 15 4 9 5 后序遍历:3 1 2 3 6 4 5 9 15 哈夫曼编码:3 被编码为 001 被编码为 0102 被编码为 0114 被编码为 105 被编码为 11

 

转载于:https://www.cnblogs.com/sench/p/7798064.html

你可能感兴趣的文章
P2617 Dynamic Rankings
查看>>
工作学习常识1
查看>>
Linux小知识点
查看>>
VisualVM监控远程主机
查看>>
C#中检查网络是否连通的二种方法
查看>>
离线Tarjan算法-最近公共祖先问题
查看>>
数据结构(四)队列
查看>>
Android Studio导入第三方类库的方法
查看>>
Tomcat version 7.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 and 6 Web modules
查看>>
Android加速度传感器实现“摇一摇”,带手机振动 .
查看>>
love2d教程7--绘图顺序
查看>>
poj 1269 Intersecting Lines
查看>>
js原型链、继承、this指向等老生常谈却依然不熟的知识点——记录解析
查看>>
python内存数据库pydblite
查看>>
设计模式-1依赖倒置原则示例
查看>>
四则运算缓冲流
查看>>
对call() apply() 方法的简单理解
查看>>
2-1. 变量
查看>>
fir.im Weekly - 可能是 iOS 审核最全面的解决方案
查看>>
银行转账存储过程和流水号生成存储过程
查看>>