哈夫曼樹(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++實現