本文用C++采用顺序存储实现求哈夫曼树(即最小生成树)的带权路径长度
努力
下面来了解一下哈夫曼树的构造以及如何求带权路径长度:
哈夫曼树为带权路径长度最小的树
哈夫曼树
哈夫曼树的顺序存储
【问题描述】
已知输入一串正整数,正整数之间用空格键分开,请建立一个哈夫曼树,以输入的数字为叶节点,求这棵哈夫曼树的带权路径长度。
【输入形式】
首先输入正整数的个数,然后接下来为接下来的正整数,正整数个数不超过10个
【输出形式】
输出相应的权值
【样例输入】
5 4 5 6 7 8
【样例输出】
69
【样例说明】
【评分标准】
注意n个叶子结点的哈夫曼树共有2n-1个结点
用到以下自定义函数:
一、选择两个其双亲域为0且权值最小的结点,并返回他们在HT中的序号num1和num2:
先选出第一个最小的,再选第二个,我都不敢相信此函数是耗时最长的,可能由于太简单就不重视了,结果在这出现好多问题
void Select(HuffmanTree HT, int n, int *min1, int *min2)
{
//选出第一个最小值结点
for (int i = 1; i <= n; i++)
{
if (HT[i].parent == 0)
{
*min1 = i;
break;
}
}
for (int i = 1; i <= n; i++)
{
if ((HT[i].weight < HT[*min1].weight) && (HT[i].parent == 0))
{
*min1 = i;
}
}
//选出与第一个结点不重复的第二个最小值结点
for (int i = 1; i <= n; i++)
{
if ((HT[i].parent == 0) && (i != *min1))
{
*min2 = i;
break;
}
}
for (int j = 1; j <= n; j++)
{
if ((HT[j].weight < HT[*min2].weight) && (j != *min1) && (HT[j].parent == 0))
{
*min2 = j;
}
}
}
二、构造哈夫曼树(采用顺序存储):
//构造哈夫曼树
void CreateHuffmanTree(HuffmanTree &HT, int n)
{
if (n <= 1)
{
return;
}
int m = 2 * n - 1;
HT = new HTNode[m + 1];//0号单元未用
for (int i = 1; i <= m; i++)//将1-m号单元中的双亲,左孩子,右孩子的下标都初始化为0
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for (int i = 1; i <= n; i++)//输入前n个单元中叶子结点的权值
{
cin >> HT[i].weight;
}
int min1;
int min2;
for (int i = n + 1; i <= m; i++)//通过n-1次选择修改,来创建哈夫曼树
{
Select(HT, i - 1, &min1, &min2);//在HT[k](1<=k<=i-1)中选择两个其双亲域为0且权值最小的结点,并返回他们在HT中的序号num1和num2
HT[min1].parent = i;//得到新结点,将双亲域由0变为i
HT[min2].parent = i;
HT[i].lchild = min1;//num1,num2分别作为i的左右孩子
HT[i].rchild = min2;
HT[i].weight = HT[min1].weight + HT[min2].weight;//i的权值为左右孩子权值之和
}
}
三、求权重和:
//求权重
int Weight(HuffmanTree HT, int n)
{
int sum = 0;
for (int i = 1; i <= n; i++)
{
int j = i;
int num = 0;//计算从叶子结点到根结点经过几条边
while (HT[j].parent != 0)//跟结点双亲域为0
{
j = HT[j].parent;
num = num + 1;
}
sum = sum + num * HT[i].weight;//求权重
}
return sum;
}
其实刚开始我是用C语言编的,不过我动态创建了一个数组后,它只让我访问下标为0的结点,后来改成C++用了引用类型,而且在C++中,在动态创建了一个数组后,千万不要用HT[i]->来指代结构体中的元素,应该用HT[i].来代替,这又是我碰到的一个小问题,希望对大家有所帮助。
(我试了一下C语言换成(*HT[i].)也无法运行出结果)
完整代码如下(附详细注释):
#include
using namespace std;
typedef struct HTNode
{
int weight;
int parent;
int lchild;
int rchild;
}HTNode, *HuffmanTree;
//选择两个其双亲域为0且权值最小的结点,并返回他们在HT中的序号num1和num2
void Select(HuffmanTree HT, int n, int *min1, int *min2)
{
//选出第一个最小值结点
for (int i = 1; i <= n; i++)
{
if (HT[i].parent == 0)
{
*min1 = i;
break;
}
}
for (int i = 1; i <= n; i++)
{
if ((HT[i].weight < HT[*min1].weight) && (HT[i].parent == 0))
{
*min1 = i;
}
}
//选出与第一个结点不重复的第二个最小值结点
for (int i = 1; i <= n; i++)
{
if ((HT[i].parent == 0) && (i != *min1))
{
*min2 = i;
break;
}
}
for (int j = 1; j <= n; j++)
{
if ((HT[j].weight < HT[*min2].weight) && (j != *min1) && (HT[j].parent == 0))
{
*min2 = j;
}
}
}
//构造哈夫曼树
void CreateHuffmanTree(HuffmanTree &HT, int n)
{
if (n <= 1)
{
return;
}
int m = 2 * n - 1;
HT = new HTNode[m + 1];//0号单元未用
for (int i = 1; i <= m; i++)//将1-m号单元中的双亲,左孩子,右孩子的下标都初始化为0
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for (int i = 1; i <= n; i++)//输入前n个单元中叶子结点的权值
{
cin >> HT[i].weight;
}
int min1;
int min2;
for (int i = n + 1; i <= m; i++)//通过n-1次选择修改,来创建哈夫曼树
{
Select(HT, i - 1, &min1, &min2);//在HT[k](1<=k<=i-1)中选择两个其双亲域为0且权值最小的结点,并返回他们在HT中的序号num1和num2
HT[min1].parent = i;//得到新结点,将双亲域由0变为i
HT[min2].parent = i;
HT[i].lchild = min1;//num1,num2分别作为i的左右孩子
HT[i].rchild = min2;
HT[i].weight = HT[min1].weight + HT[min2].weight;//i的权值为左右孩子权值之和
}
}
//求权重
int Weight(HuffmanTree HT, int n)
{
int sum = 0;
for (int i = 1; i <= n; i++)
{
int j = i;
int num = 0;//计算从叶子结点到根结点经过几条边
while (HT[j].parent != 0)//跟结点双亲域为0
{
j = HT[j].parent;
num = num + 1;
}
sum = sum + num * HT[i].weight;//求权重
}
return sum;
}
int main()
{
int n;
cin >> n;
HTNode *HT;
HT = new HTNode[2 * n + 1];
CreateHuffmanTree(HT, n);
int sum = Weight(HT, n);
cout << sum;
return 0;
}
测试结果
越努力,越幸运
end~~~