哈夫曼树(Huffman树)原理分析及实现
作者:互联网
哈夫曼树(Huffman树)原理分析及实现
1 构造原理
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:
(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
显然对n个权值,构造哈夫曼树树需要合并n-1次,形成的树结点总数为2n-1;
例如:A:60, B:45, C:13 D:69 E:14 F:5 G:3
第一步:找出字符中最小的两个,小的在左边,大的在右边,组成二叉树。
F和G最小,因此如图,从字符串频率计数中删除F与G,并返回G与F的和 8给频率表
重复第一步:
编码规则:添加 0 和 1,规则左0 右1
2 代码实现
根据哈夫曼树的构造原理,为方便实现,我们使用数组来存储每个结点,其命名为Tree;
2.1 节点结构
节点具有以下结构:
//结点结构
struct Node {
int val{0}; //节点的值
Node* left{nullptr}; //节点的左孩子
Node* right{nullptr}; //节点的右孩子
Node* parent{nullptr}; //节点的父节点
explicit Node(int value) : val(value) {}
Node(int value, Node* pleft, Node* pRight, Node* pParent)
: val(value), left(pleft), right(pRight), parent(pParent) {}
};
2.2 类的实现
Huffman.h
#include <iostream>
#include <queue>
#include <string>
#include <vector>
using namespace std;
struct Node {
int val{0}; //节点的值
Node* left{nullptr}; //节点的左孩子
Node* right{nullptr}; //节点的右孩子
Node* parent{nullptr}; //节点的父节点
explicit Node(int value) : val(value) {}
Node(int value, Node* pleft, Node* pRight, Node* pParent)
: val(value), left(pleft), right(pRight), parent(pParent) {}
};
class Compare //比较类,用于构造Node*类型的priority_queue
{
public:
bool operator()(Node* a, Node* b) {
return a->val > b->val; //结点的值越小越靠前
}
};
class HuffmanTree {
public:
HuffmanTree();
~HuffmanTree();
Node* Create();
void PreOrder(Node* pNode);
void InOrder(Node* pNode);
void PostOrder(Node* pNode);
void Encode(Node* pNode, string code);
private:
Node* root; // 哈夫曼树头
priority_queue<Node*, vector<Node*>, Compare> nodes;
void destroyTree(Node* pNode);
};
Hufman.cpp
#include "Huffman.h"
HuffmanTree::HuffmanTree() {
// priority_queue没有clear
while (!nodes.empty()) nodes.pop();
int a[] = {4, 3, 5, 8, 7, 9};
int len = sizeof(a) / sizeof(a[0]);
for (int i = 0; i < len; i++) { nodes.push(new Node(a[i])); }
root = nullptr;
}
HuffmanTree::~HuffmanTree() {
destroyTree(root);
}
void HuffmanTree::destroyTree(Node* pNode) {
if (pNode == nullptr)
return;
destroyTree(pNode->left);
destroyTree(pNode->right);
delete pNode;
}
Node* HuffmanTree::Create() {
while (nodes.size() > 1) {
Node* p1 = nodes.top();
nodes.pop();
Node* p2 = nodes.top();
nodes.pop();
Node* cur = new Node(p1->val + p2->val);
cur->left = p1;
cur->right = p2;
p1->parent = cur;
p2->parent = cur;
nodes.push(cur);
}
root = nodes.top();
nodes.pop();
return root;
}
void HuffmanTree::PreOrder(Node* pNode) {
if (pNode == nullptr)
return;
cout << pNode->val << " ";
PreOrder(pNode->left);
PreOrder(pNode->right);
}
void HuffmanTree::InOrder(Node* pNode) {
if (pNode == nullptr)
return;
InOrder(pNode->left);
cout << pNode->val << " ";
InOrder(pNode->right);
}
void HuffmanTree::PostOrder(Node* pNode) {
if (pNode == nullptr)
return;
PostOrder(pNode->left);
PostOrder(pNode->right);
cout << pNode->val << " ";
}
void HuffmanTree::Encode(Node* pNode, string code) {
//叶子节点的处理
if (pNode->left == nullptr && pNode->right == nullptr)
cout << pNode->val << " 被编码为 " << code << endl;
if (pNode->left) {
//左子树,编码code添加'0'
code += "0";
Encode(pNode->left, code);
//编码code删除'0'
code.erase(code.end() - 1);
}
if (pNode->right) {
//左子树,编码code添加'1'
code += "1";
Encode(pNode->right, code);
//编码code删除'1'
code.erase(code.end() - 1);
}
}
3 测试代码及输出
int main() {
HuffmanTree obj;
Node* root = obj.Create();
cout << "先序遍历: ";
obj.PreOrder(root);
cout << endl;
cout << "中序遍历: ";
obj.InOrder(root);
cout << endl;
cout << "后序遍历: ";
obj.PostOrder(root);
cout << endl;
cout << "哈夫曼编码: ";
obj.Encode(root, "");
return 0;
}
正确输出:
4 参考资料
1.哈夫曼树算法及C++实现
2.百度百科·哈夫曼树
3.数据结构:Huffman树(哈夫曼树)原理及C++实现
标签:Node,code,哈夫曼,val,nullptr,pNode,HuffmanTree,原理,Huffman 来源: https://www.cnblogs.com/paul-617/p/15765291.html