#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a, int b)
{
if (a > b)
return false;
else
return true;
}
//定义Huffman树的结点
struct HuffmanNode
{
int parent, weight;
int lchild, rchild;
};
//返回当前根结点权值的最大值给min1,避免在寻找最大值和次大值时出现错误
//[在O(n)的时间复杂度内寻找一个数组中最小的两个数值]
//(https://blog.csdn.net/qq_40064490/article/details/98730243)
int maximum(HuffmanNode *a, int start, int end)
{
int max = a[start].weight;
for (int i = start; i <= end; i++)
{
if (a[i].weight > max)
max = a[i].weight;
}
return max;
}
//搜寻次小值的下标
int search_tag(HuffmanNode *a, int start, int end, int target)
{
for (int i = start; i <= end; i++)
if (a[i].weight == target && a[i].parent == 0)
return i;
}
//select函数在结点1到i - 1中选择权值最小且parent值为零的两个结点并返回其下标
void select(HuffmanNode *a, int start, int end, int *s1, int *s2)
{
int min1, min2, i;//min1为最小值,min2为次小值
min1 = min2 = maximum(a,start,end);
for (i = start; i <= end; i++)
{
if (a[i].parent == 0)
{
if (min1 > a[i].weight)
{
min2 = min1;
min1 = a[i].weight;
*s1 = i;
*s2 = search_tag(a, 1, end, min2);
}
else if (a[i].weight < min2)
{
min2 = a[i].weight;
*s2 = i;
}
}
}
cout << "min1 = " << min1 << " min2 = " << min2 << endl;
}
int main()
{
int n = 5, m = 2 * n - 1;//n代表字符个数,m代表Huffman树的结点个数
int s1, s2;//记录最小值和次小值结点下标
HuffmanNode *a = (HuffmanNode *)malloc((m + 1) * sizeof(HuffmanNode));
cout << "请输入要构造Huffman树的五个结点的权值:" << endl;
for (int i = 1; i <= 5; i++)//0号单元空出
{
cin >> a[i].weight;//输入每个字符的权值
a[i].parent = a[i].lchild = a[i].rchild = 0;//初始化
}
for (int i = n + 1; i <= m; i++)
//对Huffman树中的非叶子结点进行初始化
a[i].weight = a[i].parent = a[i].lchild = a[i].rchild = 0;
//构造Huffman树
for (int i = n + 1; i <= m; i++)
{
select(a, 1, i - 1, &s1, &s2);
a[i].weight = a[s1].weight + a[s2].weight;
a[i].lchild = s1;
a[i].rchild = s2;
a[s1].parent = a[s2].parent = i;
cout << "s1 = " << s1 << " s2 = " << s2 << endl;
}
cout << "NO. " << "parent: " << "lchild: " << "rchild: " << "weight: " << endl;
for (int i = 1; i <= m; i++)
cout <<i << " " << a[i].parent << " " << a[i].lchild << " " << a[i].rchild
<< " " << a[i].weight << endl;
system("pause");
return 0;
}
当得到一棵Huffman树后,便可对其叶子结点进行Huffman编码(前缀编码),其定义为从根结点到叶子结点的路径上分支字符组成的字符串作为该叶子结点字符的编码(约定左分支表示字符‘0’,右分支表示字符‘1’)。在由叶子结点向根结点移动的过程中,Huffman编码也就倒序生成了,故可以采用字符数组以及倒序移动的指针来完成Huffman编码的输出。
//利用已经构造的Huffman树进行Huffman编码
void HuffmanCoding(HuffmanNode *a, int i, int n)
{
//具有n个叶结点的Huffman树每个叶结点的最长编码路径为n - 1
char *c = (char*)malloc(sizeof(char) * n);
*(c + n - 1) = '\0';//编码空间结束符
int head = n - 1;//head指向编码的头字符
HuffmanNode p1 = a[i], p2 = a[p1.parent];//p2为p1的父结点
while (p1.parent != 0)
{
if (p2.lchild == i)
{
c[--head] = '0';
i = p1.parent;//i的变化十分重要,因为p1,p2是向根结点移动的
//所以在比较完当前结点后,i要继续指向p1结点
}
else if (p2.rchild == i)
{
c[--head] = '1';
i = p1.parent;
}
p1 = p2;
p2 = a[p1.parent];//由叶子结点向根结点移动
}
cout << c + head;//输出当前字符的Huffman编码
head = n - 1;//编码空间清零
}
int main()
{
、、、 、、、
、、、 、、、
、、、 、、、
for (int i = 1; i <= n; i++)
HuffmanCoding(a, i, n);//对每个字符依次进行Huffman编码并输出
system("pause");
return 0;
}