哈夫曼树结构的实现
存储对象:存储某段信息里每个信息的权值,权值存储在树的叶子节点。
节点之间的关系:
1:每个非叶子节点的权值等于左节点与右节点的权值之和。
2:除叶子节点外,所有节点的左节点的权值都小于右节点。
3: 除叶子节点外,每个节点的度都为2。
特点:权值越大的节点离根节点就越近。
对关系的建模:
1:采用结构体数组表示哈夫曼树。
2:结构体元素为该节点权值以及该节点双亲节点,左节点,右节点在指针数组的位置。
对关系的约束:
1:指针数组的长度为总结点个数+1
合法操作:
根据(权值)来从根节点开始创造树
根据哈夫曼树读取哈夫曼编码
根据权值创建哈夫曼树+实现哈夫曼编码
创建哈夫曼树
step1: 创建结构体数组,并初始化叶子节点和非叶子节点
step2: 找到两个根节点权值最小的二叉树,作为新树的左树和右树生成新树
step3: 删除这两个选中的树,并把新树加入森林。
step4: 重复step2和step3直到森林里只有一棵树
哈夫曼编码实现:
step1:构建二维字符数组存储哈夫曼编码
step2:从叶子节点走到根节点,子节点是双亲节点的左节点就记录0,右节点就为1,逆序记录。
#include<iostream>
#include<string.h>
using std::cin;
using std::cout;
using std::endl;
typedef char** HuffmanCode;
typedef class
{
public:
unsigned int weight;//节点权重
unsigned int parent, left, right;//双亲节点,左节点,右节点在指针数组里的位置
}HNode, *HuffmanTree;
/*
p_HT为结构体指针,s1,s2是没有父节点的节点中最小权值的两个节点的位置,且s1小于s2,end为存放节点的前一个位置
该函数的目的是在还未有父节点的节点中,找到两个权值最小的节点,并确定它们的位置
*/
void SelectMin(HuffmanTree p_HT, int* s11, int* s22, int end)
{
int m1, m2, index1, index2;
HuffmanTree p = p_HT;
m1 = 10000; m2 = 10000;
index1 = 0; index2 = 0;
for (int i = 1; i <= end; i++)
{
if ((p + i)->parent != 0) { continue; }
else if ((p + i)->weight < m1)
{
m2 = m1;//假设m1小于m2
m1 = (p + i)->weight;
index2 = index1;
index1 = i;
}
else if ((p + i)->weight <= m2)
{
m2 = (p + i)->weight;
index2 = i;
}
}
*s11 = index1;
*s22 = index2;
}
/*
HT为二级指针,指针元素所指向的是为HNode结构体的指针数组,weight为权重数组; n为叶子节点的个数
该函数的目的就是建造一个哈夫曼树
*/
void CreatHuffTree(HuffmanTree* HT, int* weight, int n)
{
int NodeNumber = 2 * n - 1;
*HT = new HNode[NodeNumber + 1];//创建指针数组
HuffmanTree p = *HT;//创建遍历结构体指针
(p + 0)->weight = 0;
for (int i = 1; i <= n; i++)
{
(p + i)->weight = *(weight + i - 1);
(p + i)->parent = 0;
(p + i)->left = 0;
(p + i)->right = 0;
}
for (int i = n + 1; i <= NodeNumber; i++)
{
(p + i)->weight = 0;
(p + i)->parent = 0;
(p + i)->left = 0;
(p + i)->right = 0;
}
for (int i = n + 1; i <= NodeNumber; i++)//树里有n-1个度为2的节点,所以可以循环n-1次
{
int s1, s2;
SelectMin(*HT, &s1, &s2,i - 1);//为啥要i-1,因为i-1之后权重全部为0。
(p + i)->weight = (p + s1)->weight + (p + s2)->weight;
(p + i)->left = s1;
(p + i)->right = s2;
(p + s1)->parent = i;
(p + s2)->parent = i;
}
p = NULL;
}
int main()
{
HuffmanTree T;
HuffmanCode C;
int n;
cin >> n;//读取叶子结点个数
int* w = new int[n];
for (int i = 0; i < n; i++) { cin >> *(w + i); }//读取权值
CreatHuffTree(&T, w, n);
//CreatHauffmanCoding(&C, T, n);
for (int i = 1; i <= 2 * n - 1; i++)
{
cout << T[i].weight << "||" << T[i].parent << " " << T[i].left << " " << T[i].right << endl;
}
int aa;
aa = 10;
return 0;
}
结果验证:
哈夫曼编码实现:
#include<iostream>
#include<string>
using std::cin;
using std::cout;
using std::endl;
typedef char** HuffmanCode;
typedef class
{
public:
unsigned int weight;//节点权重
unsigned int parent, left, right;//双亲节点,左节点,右节点在指针数组里的位置
}HNode, *HuffmanTree;
/*
p_HT为结构体指针,s1,s2是没有父节点的节点中最小权值的两个节点的位置,且s1小于s2,end为存放节点的前一个位置
该函数的目的是在还未有父节点的节点中,找到两个权值最小的节点,并确定它们的位置
*/
void SelectMin(HuffmanTree p_HT, int* s11, int* s22, int end)
{
int m1, m2, index1, index2;
HuffmanTree p = p_HT;
m1 = 10000; m2 = 10000;
index1 = 0; index2 = 0;
for (int i = 1; i <= end; i++)
{
if ((p + i)->parent != 0) { continue; }
else if ((p + i)->weight < m1)
{
m2 = m1;//假设m1小于m2
m1 = (p + i)->weight;
index2 = index1;
index1 = i;
}
else if ((p + i)->weight <= m2)
{
m2 = (p + i)->weight;
index2 = i;
}
}
*s11 = index1;
*s22 = index2;
}
/*
HT为二级指针,指针元素所指向的是为HNode结构体的指针数组,weight为权重数组; n为叶子节点的个数
该函数的目的就是建造一个哈夫曼树
*/
void CreatHuffTree(HuffmanTree* HT, int* weight, int n)
{
int NodeNumber = 2 * n - 1;
*HT = new HNode[NodeNumber + 1];//创建指针数组
HuffmanTree p = *HT;//创建遍历结构体指针
(p + 0)->weight = 0;
for (int i = 1; i <= n; i++)
{
(p + i)->weight = *(weight + i - 1);
(p + i)->parent = 0;
(p + i)->left = 0;
(p + i)->right = 0;
}
for (int i = n + 1; i <= NodeNumber; i++)
{
(p + i)->weight = 0;
(p + i)->parent = 0;
(p + i)->left = 0;
(p + i)->right = 0;
}
for (int i = n + 1; i <= NodeNumber; i++)//树里有n-1个度为2的节点,所以可以循环n-1次
{
int s1, s2;
SelectMin(*HT, &s1, &s2,i - 1);//为啥要i-1,因为i-1之后权重全部为0。
(p + i)->weight = (p + s1)->weight + (p + s2)->weight;
(p + i)->left = s1;
(p + i)->right = s2;
(p + s1)->parent = i;
(p + s2)->parent = i;
}
p = NULL;
}
/*
HC为指向二维字符数组的指针,p_HT为数组指针,是存储哈夫曼树的数组的第一个元素,n是叶子节点数
该函数的目的是根据哈夫曼树生成哈夫曼编码
*/
void CreatHauffmanCoding(HuffmanCode* HC, HuffmanTree p_HT, int n)
{
int m = 2 * n - 1;
int start, c, j;
*HC = new char* [n + 1];//因为哈夫曼树的结构体数组0单元不存储,所以为了相对应,则分配n+1
char* cd = new char[n];
for (int i = 1; i <= n; i++)
{
start = n - 1;
cd[start] = '\0';
c = i;//c就是该节点在顺序表里的位置
j = (p_HT + i)->parent;//j代表的是该节点双亲节点在顺序表里的位置
while (j)
{
--start;
if ((p_HT + j)->left == c)
{
cd[start] = '0';
}
else { cd[start] = '1'; }
c = j;
j = (p_HT + c)->parent;
}
(*HC)[i] = new char[n - start];
strcpy_s(*(*(HC) + i), n - start, &cd[start]);
}
delete[] cd;
cd = NULL;
}
int main()
{
HuffmanTree T;
HuffmanCode C;
int n;
cin >> n;//读取叶子结点个数
int* w = new int[n];
for (int i = 0; i < n; i++) { cin >> *(w + i); }//读取权值
CreatHuffTree(&T, w, n);
CreatHauffmanCoding(&C, T, n);
for (int i = 1; i <= n; i++)
{
cout << T[i].weight << "||" << *(C + i) << endl;;
}
int aa;
aa = 10;
return 0;
}
验证:
参考文章:
调试过程中的问题