一天一个算法,边回想算法细节,边捡回C++,试验性程序,留作记念。
哈夫曼树概念
哈夫曼(Huffman)树又称最优二叉树。它是n个带权叶子结点构成的二叉树中,带权路径长度WPL最小的二叉树。因为构造这种树的算法是最早由哈夫曼于1952年提出的,所以被称之为哈夫曼树。
二叉树的性质
二叉树中有五点性质非常重要,需要记住。
性质1:在二叉树的第 i 层上至多有2^(i-1)个结点
性质2:深度为k的二叉树至多有2^k-1个结点
性质3:对任何一颗二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1
性质4:具有n个结点的完全二叉树的深度为[log(n)]+1([x]表示不大于x的最大整数)
性质5:如果对一棵有n个结点的完全二叉树(其深度为[log(n)]+1)的结点按层序编号(从第1层到第[log(n)]+1层,每层从左到右),对任一结点i(1<=i<=n)有:
(1).如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点[i/2]
(2).如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i
(3).如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1
基本术语
结点的权
“权”就相当于“重要度”,我们形象的用一个具体的数字来表示,然后通过数字的大小来决定谁重要,谁不重要。
路径
树中从“一个结点”到“另一个结点”之间的分支。
路径长度
一个路径上的分支数量。
树的路径长度
从树的根节点到每个节点的路径长度之和。
节点的带权路径路径长度
其实也就是该节点到根结点的路径长度乘以该节点的权。
树的带权路径长度
树中各个叶节点的路径长度*该叶节点的权的和,常用WPL(Weight Path Length)表示。
构建方法
第一步: 我们将所有的节点都作为独根结点。
第二步: 我们将最小的C和A组建为一个新的二叉树,权值为左右结点之和。
第三步: 将上一步组建的新节点加入到剩下的节点中,排除上一步组建过的左右子树,我们选中B组建新的二叉树,然后取权值。
第四步: 同上。
哈夫曼树的结构
typedef struct HuffmanNode
{
int weight;
int lChild, rChild, parent;
} *pNode;
哈夫曼树的各种操作
构建哈夫曼树用到的获取最小两个权值下标函数
void HuffmanTree::SelectNode(int bg, int * min1, int * min2){
int index = bg - _n;
//每次进入实际上都向后移了一位
for (int i = 0; i < bg; i++){
if (hf[index] == huffman[i].weight){
*min1 = i;
break;
}
}
for (int i = 0; i < bg; i++){
//注意有可能合并后的值会与权值重复
if (*min1 != i && hf[index + 1] == huffman[i].weight){
*min2 = i;
break;
}
}
int newNode = hf[index] + hf[index + 1];
int i = index + 2;
//将新产生的权值插入到排序数组合适的位置
for (; i < _n; i++){
if (newNode > hf[i])
hf[i - 1] = hf[i];
else
break;
}
hf[i - 1] = newNode;
}
哈夫曼树的构建
HuffmanTree::HuffmanTree(int * keys, int N)
{
using namespace std;
this->_n = N;
//总的节点数
this->_m = 2 * N - 1;
huffman = new HuffmanNode[_m];
hf = new int[N];
for (int i = 0; i < _m; i++){ //初始化
if (i < N){ //前N个为权重叶子节点
huffman[i].weight = keys[i];
if (i == 0)
hf[0] = keys[0];
else{
int j = i - 1;
//从小到大插入到排序数组中
for (; j >= 0; j--){
if (keys[i] < hf[j]){
hf[j + 1] = hf[j];
}
else
break;
}
hf[j + 1] = keys[i];
}
}
else //后面的为辅助结点,非叶子节点
huffman[i].weight = -1;
huffman[i].lChild = -1;
huffman[i].rChild = -1;
huffman[i].parent = -1;
}
//创建,从第一个辅助节点开始
for (int i = _n; i < _m; i++){
int min1;
int min2;
SelectNode(i, &min1, &min2);
huffman[min1].parent = i;
huffman[min2].parent = i;
huffman[i].lChild = min1;
huffman[i].rChild = min2;
huffman[i].weight = huffman[min1].weight + huffman[min2].weight;
}
}
生成哈夫曼编码模版数组
vector<string> HuffmanTree::HuffmanCoding(int len){
int cur = 0;
int parent = 0;
vector<string> hfcode = vector<string>(len);
for (int i = 0; i < len; i++){
string tmp = "";
cur = i;
parent = huffman[cur].parent;
while (parent>=0)
{
if (cur == huffman[parent].lChild){
tmp += "0";
}
else
{
tmp += "1";
}
cur = parent;
parent = huffman[parent].parent;
}
reverse(tmp.begin(), tmp.end());
hfcode[i] = tmp;
}
return hfcode;
}
哈夫曼编码
string HuffmanTree::Encode(vector<string> hfCode, vector<char> alphabet, string str){
string encodeStr = "";
for (int i = 0; i<str.length(); i++) {
for (int j = 0; j < alphabet.size(); j++)
if (str.at(i) == alphabet[j])
encodeStr += hfCode[j];
}
return encodeStr;
}
哈夫曼解码
string HuffmanTree::Decode(int len, vector<char> alphabet, string str){
//最后一个点实际上为哈夫曼树的根节点
int m = 2 * len - 1;
string decodeStr = "";
for (int i = 0; i<str.length(); ) {
int j;
//从根开始解码,一直找到叶子节点为止
for (j = m - 1; (huffman[j].lChild >= 0 || huffman[j].rChild >= 0);){
if (str.at(i) == '0')
j = huffman[j].lChild;
else
j = huffman[j].rChild;
i++;
}
decodeStr += alphabet[j];
}
return decodeStr;
}
类定义(头文件)
#pragma once
#include <string>
#include <vector>
using namespace std;
class HuffmanTree
{
private:
typedef struct HuffmanNode
{
int weight;
int lChild, rChild, parent;
} *pNode;
pNode huffman;
int * hf; //用来排序
int _n,_m;
void SelectNode(int i,int *min1,int *min2);
public:
HuffmanTree(int * keys, int N);
vector<string> HuffmanCoding(int len);
string Encode(vector<string> hfCode, vector<char> alphabet, string str);
string Decode(int len, vector<char> alphabet, string str);
~HuffmanTree();
};
用法示例
#include <conio.h>
#include "HuffmanTree.h"
int main()
{
int arrs[] = { 5, 7, 2, 13 };
vector<char> alphabet = { 'A', 'B', 'C', 'D' };
int len = sizeof(arrs) / sizeof(arrs[0]);
HuffmanTree hf = HuffmanTree(arrs, len);
string testCode = "DBDBDABDCDADBDADBDADACDBDBD";
string encodeStr = hf.Encode(hf.HuffmanCoding(len), alphabet, testCode);
printf("%s\n", encodeStr.c_str());
string decodeStr = hf.Decode(len, alphabet, encodeStr);
printf("%s\n", decodeStr.c_str());
getch();
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。