题目描述
给定n个权值,根据这些权值构造huffman树,并进行huffman编码
参考课本算法,注意数组访问是从位置1开始
要求:赫夫曼的构建中,默认左孩子权值不大于右孩子权值
输入
第一行输入t,表示有t个测试实例
第二行先输入n,表示第1个实例有n个权值,接着输入n个权值,权值全是小于1万的正整数
依此类推
输出
逐行输出每个权值对应的编码,格式如下:权值-编码
即每行先输出1个权值,再输出一个短划线,再输出对应编码,接着下一行输入下一个权值和编码。
以此类推
输入样例1
1
5 15 4 4 3 2
输出样例1
15-1
4-010
4-011
3-001
2-000
赫夫曼编码
构建赫夫曼树
构建一个子树的步骤,以下循环 n - 1 次,就可以把一片森林合并成一颗树。
初始化最小和次小值:
- 内部循环遍历森林的节点 (
forest[j]),找到第一个非空节点作为minn,接着找到下一个非空节点作为minnSub,这两个节点分别表示初始的最小和次小权重节点。
更新最小和次小值:
- 从
minnSub位置开始遍历森林,更新minn和minnSub的值。 - 如果当前节点权重小于
minn的权重,则将minn和minnSub交换,并更新最小值为当前节点。 - 如果当前节点权重介于
minn和minnSub之间,则将其赋值给minnSub。
建立赫夫曼树:
- 创建一个
rroot节点,并将权重赋值为minn + minnSub。 - 将
minnSub赋为NULL, 将minn赋值为temp。

HNode* createHuffmanTree(int arr[], int n) {
HNode* rroot = NULL;
// 初始化森林
HNode** forest = new HNode*[n];
for (int i = 0; i < n; i++) {
HNode* temp;
temp = new HNode;
temp->weight = arr[i];
// 将一个地址赋给了 forest[i]:
forest[i] = temp;`在这里插入代码片`
}
for(int i=0;i<n-1;i++){
// 选择两个结点:
int min = -1,subMin = -1;
choose(forest,min,subMin,n);
// 创建新的树:
rroot = new HNode; // 一定要在这里开空间。
rroot->weight = forest[min]->weight + forest[subMin]->weight;
rroot->lchild = forest[min];
rroot->rchild = forest[subMin];
forest[min] = rroot;
forest[subMin] = NULL;
}
return rroot;
}
选择结点的函数
逻辑并不复杂,但是写起来很容易错
void choose(HNode** forest,int& min,int& subMin,const int n)
{
// 找到一个非空结点赋给 min,初始化
for(int i=0;i<n;i++){
if(forest[i]){
min = i;
for(int j=i+1;j<n;j++){
if(forest[j]&&forest[j]->weight>=forest[i]->weight){
subMin = j;
break;
}else if(forest[j]&&forest[j]->weight<forest[i]->weight){
subMin = i;
min = j;
break;
}
}
break;
}
}
// cout<<"min = "<<min<<" subMin = "<<subMin<<endl;
for(int i=subMin+1;i<n;i++){
if(!forest[i]) continue;
if(forest[i]->weight<forest[min]->weight){
subMin = min;
min = i;
}else if(min!=i&&forest[i]->weight<forest[subMin]->weight&&forest[i]->weight>=forest[min]->weight){
subMin = i;
}
}
// cout<<"After Update: "<<"min = "<<min<<" subMin = "<<subMin<<endl;
}
哈夫曼编码
我是利用前序遍历编码的。。
void huffmanCoding( HNode*& node,string code ="")
{
if(node == NULL) return;
node->Hconding = code;
huffmanCoding(node->lchild,code + "0");
huffmanCoding(node->rchild,code + "1");
}
输出
- 根据数组的顺序在赫夫曼树中寻找它的位置
- 为了避免重复输出,找到之后将权重赋值为-1
- 关键就在于如何在赫夫曼树中找到这个位置,我用的是前序遍历搜索
HNode* Find(HNode*& node, int target)
{
HNode* root = NULL; // 初始化返回值为空指针
if (!node) return root; // 如果当前节点为空,返回空指针(递归终止条件)
if (node->weight == target) { // 如果当前节点的权重等于目标值,返回当前节点
// node = NULL;
node->weight = -1;
return node;
}
// 递归查找左子树
root = Find(node->lchild, target);
// 如果在左子树中找到了目标节点,直接返回
if (root != NULL) {
return root;
}
// 递归查找右子树
root = Find(node->rchild, target);
return root; // 返回找到的节点(如果有的话)
}
代码
# include <iostream>
# include <string>
using namespace std;
struct HNode
{
int weight;
HNode* lchild, * rchild;
string Hconding; // 存储编码
HNode(int w = 0, HNode* l = NULL, HNode* r = NULL) : weight(w), lchild(l), rchild(r), Hconding("") {}
};
void choose(HNode** forest,int& min,int& subMin,const int n)
{
// 找到一个非空结点赋给 min,初始化
for(int i=0;i<n;i++){
if(forest[i]){
min = i;
for(int j=i+1;j<n;j++){
if(forest[j]&&forest[j]->weight>=forest[i]->weight){
subMin = j;
break;
}else if(forest[j]&&forest[j]->weight<forest[i]->weight){
subMin = i;
min = j;
break;
}
}
break;
}
}
// cout<<"min = "<<min<<" subMin = "<<subMin<<endl;
for(int i=subMin+1;i<n;i++){
if(!forest[i]) continue;
if(forest[i]->weight<forest[min]->weight){
subMin = min;
min = i;
}else if(min!=i&&forest[i]->weight<forest[subMin]->weight&&forest[i]->weight>=forest[min]->weight){
subMin = i;
}
}
// cout<<"After Update: "<<"min = "<<min<<" subMin = "<<subMin<<endl;
}
HNode* createHuffmanTree(int arr[], int n) {
HNode** forest = new HNode*[n]; // 初始化森林
HNode* rroot = NULL;
for (int i = 0; i < n; i++) {
HNode* temp;
temp = new HNode;
temp->weight = arr[i];
forest[i] = temp;
}
for(int i=0;i<n-1;i++){
// 选择两个结点:
int min = -1,subMin = -1;
choose(forest,min,subMin,n);
// 创建新的树:
rroot = new HNode; // 一定要在这里开空间。
rroot->weight = forest[min]->weight + forest[subMin]->weight;
rroot->lchild = forest[min];
rroot->rchild = forest[subMin];
forest[min] = rroot;
forest[subMin] = NULL;
}
return rroot;
}
void huffmanCoding( HNode*& node,string code ="")
{
if(node == NULL) return;
node->Hconding = code;
huffmanCoding(node->lchild,code + "0");
huffmanCoding(node->rchild,code + "1");
}
// 利用前序遍历搜索:
HNode* Find(HNode*& node, int target)
{
HNode* root = NULL; // 初始化返回值为空指针
if (!node) return root; // 如果当前节点为空,返回空指针(递归终止条件)
if (node->weight == target) { // 如果当前节点的权重等于目标值,返回当前节点
// node = NULL;
node->weight = -1;
return node;
}
// 递归查找左子树
root = Find(node->lchild, target);
// 如果在左子树中找到了目标节点,直接返回
if (root != NULL) {
return root;
}
// 递归查找右子树
root = Find(node->rchild, target);
return root; // 返回找到的节点(如果有的话)
}
int main()
{
int t;
cin >> t;
while(t--){
int n;
cin>>n;
int* arr = new int [n];
for(int i = 0; i < n; i++){
cin >> arr[i];
}
HNode* root = createHuffmanTree(arr, n);
huffmanCoding(root);
// 输出,我实在是不知道这个输出的顺序是怎么样的::
for(int i=0;i<n;i++){
cout<<arr[i]<<"-"<<Find(root,arr[i])->Hconding<<endl;
}
}
}
关于内存分配一点小问题(初始化森林)
- forset 数组里面存放的是地址,因而是一个二维数组。
- 下面temp循环开空间,并把地址赋给forest[i]就是给二维数组开第二个空间。
HNode** forest = new HNode*[n]; // 初始化森林
for (int i = 0; i < n; i++) {
HNode* temp;
temp = new HNode;
temp->weight = arr[i];
forest[i] = temp;
}
- 如果不使用temp
HNode** forest = new HNode*[n]; // 初始化森林
for(int i=0;i<n;i++)
forest[i] = new HNode;
for (int i = 0; i < n; i++) {
forest[i]->weight = arr[i];
}
1572

被折叠的 条评论
为什么被折叠?



