题目描述:
给定一个文本中n个字符的出现频率。设计n个字符的哈夫曼编码。
输入描述:
输入包括多组数据。每组数据的第一行是一个整数n(1 <= n <= 100),代表不同字符的总数,n = 0意味着输入结束;接下来有n行,其中的每一行包含一个字符及其出现频率。
输出描述:
对每组数据,输出哈夫曼编码的平均位数,即l1 * f1 + l2 * f2 + ... + ln * fn,其中li是第i个字符的哈夫曼编码的位数,fi是第i个字符出现的频率。
样例输入:
5
A 35
B 10
C 20
D 20
_ 15
3
x 20
Y 50
Z 5
0
上代码:
#include<iostream>
#include<string>
using namespace std;
int n;
//定义一个结构体,用作结点信息存储
struct ElemType {
string code=""; //用来存储该字符的哈夫曼编码
char data; //用来存储叶子节点的字符
int weight; //假设权值为整数,用来存储叶子结点的权重
int parent, lchild, rchild; //用来存储父节点,以及左右孩子结点
int index = -1;//用来存储原来结构体数组下标,以便于后面给i1,i2赋值
int LR; //用来唯一标记该结点是左孩子还是右孩子
};
void Select(ElemType huffTree[], int &i1, int &i2) { //选择排序,以便于通过每个根节点的权重来获取两个权重最小的根节点下标志
int count = 0; //用来计数,方便后边给i1和i2赋值
ElemType reserve[2 * n - 1];
for (int i = 0; i < 2 * n - 1; i++) { //将结构体数组的元素下标初始化
huffTree[i].index = i; //将结构体里面的index给初始化,防止排序过后顺序变化而导致获取的原来下标不是之前的数组小标
reserve[i] = huffTree[i];//同时将reserve[i]用来暂时保存传入的哈夫曼数,以便于后边将这一个给赋值回来,
}
for (int i = 0; i < 2 * n - 1; i++) { //使用冒泡排序对数组通过权重进行从小到大排序
for (int j = 0; j < 2 * n - 1 - i; j++) {
if (huffTree[j].weight > huffTree[j + 1].weight) {
ElemType temp = huffTree[j + 1];
huffTree[j + 1] = huffTree[j];
huffTree[j] = temp;
}
}
}
for (int i = 0; i < 2 * n - 1; i++) { //遍历huffTree数组,当排好了序的数组遍历到第一个结构体的parent=-1时候将其赋值给i1,之后小中断此循环,之后在进行相似操作,以获取i2
if (count == 0 && huffTree[i].parent == -1) { //定义count方便用来获取权重最小的和倒数第二小的结点
i1 = huffTree[i].index;
count = 1;
continue; //找到第一个之后暂时终止程序,以防两个最小的被赋值为同一个下标
}
if (count == 1 && huffTree[i].parent == -1) {
i2 = huffTree[i].index;
break; //找到下标后就可以中断
}
}
//排序过后将haffmanTree恢复原来顺序
for (int i = 0; i < 2*n-1; i++) {
huffTree[i] = reserve[i];
}
}
void createHuffmanTree(ElemType huffTree[], int w[], char ch[]) {
int i, k, i1, i2;//使用i1和i2用来存储权值权值最小的两个根节点
for (i = 0; i < 2 * n - 1; i++) { //刚开始所有结点设置为没有双亲和孩子,由于有n个叶子结点就有2*n-1个节点,所以创建创建2*n-1个结点用来存储
huffTree[i].parent = -1;//指向-1表示没有指向任何一个结点
huffTree[i].lchild = huffTree[i].rchild = -1;
huffTree[i].weight = 0x7fffffff; //权重赋初值为无穷大,以便于后边排序从而寻找i1和i2
huffTree[i].data = '\0';//给所有的结构体的字符赋值为空字符
}
for (i = 0; i < n; i++) { //储存叶子结点的权值和对应的字符
huffTree[i].weight = w[i];
huffTree[i].data = ch[i];
}
for (k = n; k < 2 * n - 1; k++) { //进行n-1次合并,至于为什么是n-1次,这是由于每一次合并都会产生一个新的结点,而除了叶子结点外所有的结点(n-1个)全是合并而来,所以就要进行n-1次合并
Select(huffTree, i1, i2); //权值最小的根节点下标为i1和i2
huffTree[k].weight = huffTree[i1].weight + huffTree[i2].weight; //新建立的结点权重就是两个子节点权重之和
huffTree[i1].parent = huffTree[i2].parent = k; //设置父索引是k
huffTree[k].lchild = i1; huffTree[k].rchild = i2; //设置左子索引是i1,右子索引是i2
huffTree[i1].LR = 0; huffTree[i2].LR = 1; //用来唯一的表示是左孩子还是右孩子
}
}
//根据权重来实现哈夫曼编码
void EnHuffmanCode(ElemType huffTree[]) {
for (int i = 0; i < n; i++) { //有n个叶子结点,就要进行n次编码,左子树编码为0,右子树编码为1的规则
int j = i; //利用j来存储i以便于后面不至于改变i的值
while (huffTree[i].parent != -1) { //循环至根节点停止
if (huffTree[i].LR == 0) { //如果该叶子结点的索引等于他父节点的左孩子的索引,也就是判断该结点是他父节点的左孩子,就给编码前面加上"0"
huffTree[j].code = "0" + huffTree[j].code; //如果是左孩子就在前面添加上"0"字符
i = huffTree[i].parent; //将i用父节点的下标代替
}
if (huffTree[i].LR == 1) { //如果该叶子结点的索引等于他父节点的右孩子的索引,也就是判断该结点是他父节点的右孩子,就给编码前面加上"1"
huffTree[j].code = "1" + huffTree[j].code;
i = huffTree[i].parent; //将i用父节点的下标代替
}
}
i = j; //将保存了i的j赋值给i
}
}
void printCode(ElemType huffTree[]) {
int s = 0;
for (int i = 0; i < n; i++) {
int len = huffTree[i].code.length();
s+=len*huffTree[i].weight;
}
cout<<s<<endl;
}
int main() {
while(cin>>n&&n!=0){ //以0为结束标志
char ch[n]; int w[n];
for(int i=0;i<n;i++){
cin>>ch[i]>>w[i]; //输入每个字符,以及对应的权重
}
ElemType huffTree[2 * n];
createHuffmanTree(huffTree, w, ch);
EnHuffmanCode(huffTree);
printCode(huffTree);
}
return 0;
}
输出
225
100