1.问题
问题:给定字符集 C=
和每个字符的频率
,求关于 C 的 一个最优前缀码。
2.解析
哈夫曼树的基本思想:选择权值小的叶子离根距离远。
第一步:以每个结点作为根,构造只有一个根结点的n棵二叉树,根的权值就是结点的权。
第二步:在所有二叉树中选择根的权值最小的两棵二叉树作为左右子树构造一棵新的二叉树,根的权值等于其左右子树的根的权值之和。
第三步:去掉选中的二叉树、加入新生成的二叉树。
第四步:重复2、3步,直至只剩下一棵树为止。
3.设计
int Build_Huffman_tree(unsigned int(&freqs)[NUM_CHARS], HuffNode(&Huffman_array)[MAX_SIZE], int n)
{ //n表示freqs数组中实际包含的字符种类数
char c;
int k = 0, x1, x2;
unsigned int m1, m2;
for (int i = 0; i < NUM_CHARS; i++)//把前n个叶结点的信息输入Huffman_array数组
{
if (freqs[i])
{
c = i;//还原字符
Huffman_array[k].data = c;
Huffman_array[k].freq = freqs[i];
Huffman_array[k].parent = 0;
Huffman_array[k].lchild = 0;
Huffman_array[k].rchild = 0;
k++;
}
}
for (int i = n; i < 2 * n - 1; i++)//处理剩下n-1个非叶子结点
{
Huffman_array[i].data = '#';
Huffman_array[i].freq = 0;
Huffman_array[i].parent = 0;
Huffman_array[i].lchild = 0;
Huffman_array[i].rchild = 0;
}
// 循环构造 Huffman 树
for (int i = 0; i < n - 1; i++)
{
m1 = m2 = MAX_FREQ; // m1、m2中存放两个无父结点且结点权值最小的两个结点
x1 = x2 = 0; //x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号
/* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */
for (int j = 0; j < n + i; j++)
{
if (Huffman_array[j].freq < m1 && Huffman_array[j].parent == 0)//parent 来控制该点是否被选取过了
{ //如果当前判断的结点的权值小于最小的m1,则把它赋给m1,同时
m2 = m1; //更新m1结点的下标, 保持m1是当前所有判断过的元素中是最小的
x2 = x1; //再把m1的信息赋给m2,保持m2是当前所有判断过的元素中是第二小的
m1 = Huffman_array[j].freq;
x1 = j;
}
else if (Huffman_array[j].freq < m2 && Huffman_array[j].parent == 0)
//如果当前判断的结点的权值大于等于最小的m1,但是小于m2,
{ //则只需把它赋给m2,更新m2,保持m2是当前所有判断过的元素中是第二小的
m2 = Huffman_array[j].freq;
x2 = j;
}
}
/* 设置找到的两个子结点 x1、x2 的父结点信息 */
Huffman_array[x1].parent = n + i;
Huffman_array[x2].parent = n + i;
Huffman_array[n + i].freq = Huffman_array[x1].freq + Huffman_array[x2].freq;
Huffman_array[n + i].lchild = x1;
Huffman_array[n + i].rchild = x2;
}
return 0;
}
4.分析
Build_Huffman_tree(freqs, Huffman_array, char_size);
O(nlogn)频率排序;for 循环 O(n)O(nlogn)频率排序;for 循环 O(n),插入操作 O(logn)
算法时间复杂度是 O(n logn)