#include <iostream>
#include <iomanip>
using namespace std;
const int N = 20;
typedef struct {
int weight;//权值
int parent, lchild, rchild;//双亲结点和左右孩子结点
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;//二维数组存储每个叶结点的哈夫曼编码
void Select(HuffmanTree& HT, int n, int& s1, int& s2);//每次选择出权值最小的两个结点,用s1 s2返回以用来合并新结点
void CreateHuffmanTree(HuffmanTree& HT, int* w, int n);//建立哈夫曼树,结点存在与数组w中,结点个数为n
void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n);//利用哈夫曼树编写哈夫曼编码,结点存在与数组w中,结点个数为n
void strcpy(char* HC, char* code, int start);//从code的第start位开始将code复制到HC
void DisplayHuffmanTree(HuffmanTree HT, int n);
void Select(HuffmanTree& HT, int n, int& s1, int& s2) {
int min = 0;//临时变量,用于存储权值最小的元素
for (int i = 0; i < n; i++) {//第一轮循环,寻找一个无父母的结点
if (HT[i].parent == -1) {
min = i; break;
}
}
for (int i = min + 1; i < n; i++) {//第二轮循环,遍历min之后的所有结点,将权值最小的结点存入min
if (HT[i].parent == -1)
if (HT[i].weight < HT[min].weight)
min = i;
}
s1 = min;//找到了第一个最小结点
//重复上述过程,但要确保所找到元素非s1
for (int i = 0; i < n; i++) {//第一轮循环,寻找一个无父母的结点
if (HT[i].parent == -1 && i != s1) {
min = i; break;
}
}
for (int i = min + 1; i < n; i++) {//第二轮循环,遍历min之后的所有结点,将权值最小的结点存入min
if (HT[i].parent == -1)
if (HT[i].weight < HT[min].weight && i != s1)
min = i;
}
s2 = min;//找到了第二个最小结点
}
void CreateHuffmanTree(HuffmanTree& HT, int* w, int n) {
if (n <= 1)return;//如果结点为1个或不存在则直接完成建立
int m = 2 * n - 1;//根据公式,总结点个数为2n-1个
HT = new HTNode[m];//分配m个空间用来建立哈夫曼树
if (!HT)exit(0);
for (int i = 0; i < n; i++) {//前n个结点用来存储初始的所有叶子结点
HT[i].parent = -1;
HT[i].lchild = -1;
HT[i].rchild = -1;
HT[i].weight = w[i];
}
for (int i = n; i < m; i++) {//后面的结点用来存储哈夫曼树的非叶子结点,先进行初始化
HT[i].parent = -1;
HT[i].lchild = -1;
HT[i].rchild = -1;
HT[i].weight = 0;
}
for (int i = n; i < m; i++) {
int s1, s2;
Select(HT, i, s1, s2);//从0-i-1一共i个结点中选出最小的s1和s2
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
}
void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, int* w, int n) {
HC = new char* [n];//一共n个结点,分配n个一维数组组成一个二维数组
if (!HC)exit(0);
char* code = new char[n];//编码长度不会超过结点总数
if (!code)exit(0);
code[n - 1] = '\0';//最后一位放\0
for (int i = 0; i < n; i++) {
int start = n - 1;
for (int ch = i, fa = HT[i].parent; fa != -1; ch = fa, fa = HT[fa].parent) {
//取首元素为初始孩子ch,令fa为其父母,若父母不存在(为叶子结点)则继续循环,循环一次后ch父母变为ch,fa为此时ch的父母
if (HT[fa].lchild == ch)
code[--start] = '0';//若该叶子结点为父母的左孩子,编码0,右孩子编码1
else
code[--start] = '1';
}
HC[i] = new char[n - start];
strcpy(HC[i], code, start);
}
delete[] code;
}
void strcpy(char* HC, char* code, int start)
{
int i = 0;
while (code[start] != '\0')
{
HC[i] = code[start];
i++;
start++;
}
HC[i] = '\0';
}
void DisplayHuffmanTree(HuffmanTree HT, int n)
{
int i, m;
if (n <= 1)
return;
cout << "weight parent lchild rchild\n";
m = 2 * n - 1;
for (i = 0; i < m; i++)
cout << setw(4) << HT[i].weight << setw(7) << HT[i].parent << setw(7) << HT[i].lchild << setw(7) << HT[i].rchild << endl;
}//DisplayHuffmanTree
int main()
{
HuffmanTree HT;
cout << "请输入叶子结点的个数(<=100):" << endl;
int i, n;
cin >> n;
int w[N];
cout << "请依次输入个结点的权值(为正整型值):" << endl;
for (i = 0; i < n; i++)
cin >> w[i];
CreateHuffmanTree(HT, w, n);
DisplayHuffmanTree(HT, n);
HuffmanCode HC;
HuffmanCoding(HT, HC, w, n);
cout << "各个节点元素赫夫曼编码:\n";
for (i = 0; i < n; i++) {
cout << "第" << i + 1 << "个叶子的权重:" << w[i] << ",编码:" << HC[i] << endl;
}
return 0;
}
实验样例