哈夫曼树构建的核心思路是在创建每一个结点时给结点赋权值,同时推该结点进入“优先队列”。根据优先队列的特性,取优先队列的俩顶部结点生成新结点(俩顶部结点权值相加,并作为新结点的左右子结点),俩顶部结点出队,新结点入队。若干次循环后,队列只剩下一个结点,该节点便是哈夫曼树的根节点。
哈夫曼编码的生成见代码,递归简单易懂。
树与编码构建代码如下:
#include<iostream>
#include<queue>
#include<stack>
#include<vector>
#include<string>
#include<algorithm>
#include<functional>
using namespace std;
//创建一个全局字符数组
char Arr[10000];
//创建一个哈夫曼树的类
class HuffmanTreeNode{
private:
//weight是权值,Huffmancode用来记录生成的哈夫曼编码
int weight;
string Huffmancode;
HuffmanTreeNode *lchild;
HuffmanTreeNode *rchild;
public:
//
void Set(int num, HuffmanTreeNode *left, HuffmanTreeNode *right) {
this->weight = num;
this->lchild = left;
this->rchild = right;
}
int getWeight() {
return this->weight;
}
//判断这个结点是否为叶子结点,是的话返回true
bool isLeaf(HuffmanTreeNode *root) {
return !root->lchild && !root->rchild;
}
//哈夫曼编码生成,top是数组的下标,没操作一次就往后走一位
void Create_HuffmanCode(HuffmanTreeNode *root, char *Arr, int top) {
//根的左结点不为空时进行操作
if (root->lchild) {
Arr[top] = '0';
Create_HuffmanCode(root->lchild, Arr, top + 1);
}
//根的右结点不为空时进行操作
if (root->rchild) {
Arr[top] = '1';
Create_HuffmanCode(root->rchild, Arr, top + 1);
}
//该结点是叶子节点时进行操作
if (this->isLeaf(root)) {
for (int i = 0; i < top; i++) {
if (Arr[i-1]==' '&&Arr[i]!=' ') {
break;
}
}
//arr是字符数组,top是arr中的任意长度(整型)
string code(Arr, top);
int index;
//如果字符串不存在包含关系,那么返回值就一定是npos
//这里的code.find()赋值给index后,黄色括号返回的值就是find返回的值吗
while ((index=code.find(' '))!=code.npos) {
code=code.erase(index, 1);
}
root->Huffmancode=code;
}
}
void PrintHuffmanTree(HuffmanTreeNode *root) {
queue<HuffmanTreeNode*> q;
q.push(root);
while (!q.empty()) {
HuffmanTreeNode *cur = q.front();
q.pop();
if (cur->lchild) {
q.push(cur->lchild);
}
if (cur->rchild) {
q.push(cur->rchild);
}
if (this->isLeaf(cur)) {
cout<<cur->weight<<"的哈夫曼编码为:"<<cur->Huffmancode<<endl;
}
}
}
//递归前序遍历
void PreorderTraversal(HuffmanTreeNode *T) {
if (T == NULL) {
return;
}
//访问根节点并输出
cout << T->weight << " ";
T->PreorderTraversal(T->lchild);
T->PreorderTraversal(T->rchild);
}
//递归中序遍历
void InorderTraversal(HuffmanTreeNode *T) {
if (T == NULL) {
return;
}
T->InorderTraversal(T->lchild);
cout << T->weight << " ";
T->InorderTraversal(T->rchild);
}
//递归后序遍历
void PostorderTraversal(HuffmanTreeNode *T) {
if (T == NULL) {
return;
}
T->PostorderTraversal(T->lchild);
T->PostorderTraversal(T->rchild);
cout << T->weight << " ";
}
};
struct cmp{
bool operator()(HuffmanTreeNode *node1, HuffmanTreeNode *node2) {
return node1->getWeight() > node2->getWeight();
}
};
HuffmanTreeNode *Create_HuffmanTree(int *data, int size) {
//这里的cmp是小顶栈
priority_queue<HuffmanTreeNode*, vector<HuffmanTreeNode *>, cmp> Q;
//给size个结点赋权值,并让他们入队列
for (int i = 0; i < size; i++) {
HuffmanTreeNode *root = new HuffmanTreeNode;
root->Set(data[i], NULL, NULL);
Q.push(root);
}
//注意Q是永远不为空的,最少的时候是一个结点
while (Q.size()!=1) {
HuffmanTreeNode *left = Q.top();
Q.pop();
HuffmanTreeNode *right = Q.top();
Q.pop();
HuffmanTreeNode *root = new HuffmanTreeNode;
//这个Set是关键一步,队列前两个结点权值相加,将新权值赋给新结点,然后设置新结点的左右结点
root->Set(left->getWeight() + right->getWeight(), left, right);
//将新结点入队,这里就体现出了优先队列的好处了,在队列里,不管先后插入顺序,先输出的必是最小的一个
Q.push(root);
}
//拿到顶部的根!
HuffmanTreeNode *root=Q.top();
Q.pop();
return root;
}
int main(){
int N,*data;
cout<<"请输入哈夫曼树初始结点数:"<<endl;
cin>>N;
data=new int[N];
for(int i=0; i<N; i++) {
cin>>data[i];
}
HuffmanTreeNode *root;
root=Create_HuffmanTree(data, N);
root->Create_HuffmanCode(root, Arr, N);
root->PrintHuffmanTree(root);
cout<<"前序遍历(递归):"<<endl;
root->PreorderTraversal(root);
cout<<endl;
cout << "中序遍历(递归):" << endl;
root->InorderTraversal(root);
cout << endl;
cout << "后序遍历(递归):" << endl;
root->PostorderTraversal(root);
cout << endl;
return 0;
}
输入:
请输入哈夫曼树初始结点数:
5
1 2 3 4 5
输出:
3的哈夫曼编码为: 00
4的哈夫曼编码为: 10
5的哈夫曼编码为: 11
1的哈夫曼编码为: 010
2的哈夫曼编码为: 011
前序遍历(递归):
15 6 3 3 1 2 9 4 5
中序遍历(递归):
3 6 1 3 2 15 4 9 5
后序遍历(递归):
3 1 2 3 6 4 5 9 15
over!
-------------------------------------------2021年10月29日更新-------------------------------------------
深圳大学oj题目——赫夫曼树的构建与编码
代码如下:
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
const int MaxW=9999;
//定义HuffMan树结点类
class HuffNode{
public:
int weight; //权值
int parent; //父结点下标
int leftchild; //左孩子下标
int rightchild; //右孩子下标
};
//定义哈夫曼树
class HuffMan{
private:
void MakeTree();
//从1到pos的位置找出权值最小的两个结点,把结点下标存在s1和s2中
void SelectMin(int pos,int *s1,int *s2);
public:
int len; //结点数量
int lnum; //叶子数量
HuffNode* huffTreeRoots; //哈夫曼树,用数组表示
string *huffCode; //每个字符对应的哈夫曼编码
void MakeTree(int n,int wt[]);
void Coding();
void Destroy();
};
//构建HuffMan树
void HuffMan::MakeTree(int n, int wt[]) { //参数是叶子结点数量和叶子权值
int i;
lnum=n;
len=2*n-1;
//位置从1开始计算
huffTreeRoots=new HuffNode[2*n];
huffCode=new string[lnum+1];
//哈夫曼树huffmanTree初始化
for(i=1;i<=n;i++)
huffTreeRoots[i].weight=wt[i-1];//第0号不用,从1开始编号
for(i=1;i<=len;i++){
//前n个是叶子结点,已经设值
if(i>n)
huffTreeRoots[i].weight=0;//
huffTreeRoots[i].parent=0;
huffTreeRoots[i].leftchild=0;
huffTreeRoots[i].rightchild=0;
}
MakeTree();
}
void HuffMan::SelectMin(int pos, int *s1, int *s2) {
//第i结点的权值和下标保存到w1和s1,作为第一最小值
//第i结点的权值和下标保存到w2和s2,作为第二最小值
int w1,w2,i;
w1=w2=MaxW;
//s1与s2是俩最小权值的下标
*s1=*s2=0;
for(i=1;i<=pos;i++){
if(huffTreeRoots[i].weight<w1&&huffTreeRoots[i].parent==0){
w2=w1;
*s2=*s1;
*s1=i;
w1=huffTreeRoots[i].weight;
}else if(huffTreeRoots[i].weight<w2&&huffTreeRoots[i].parent==0){
*s2=i;
w2=huffTreeRoots[i].weight;
}
}
}
void HuffMan::MakeTree() {
int i,s1,s2;
//构造哈夫曼树的n-1个非叶结点
for(i=lnum+1;i<=len;i++){
//想象从左往右有一个数组
SelectMin(i-1,&s1,&s2);
huffTreeRoots[s1].parent=i;
huffTreeRoots[s2].parent=i;
huffTreeRoots[i].leftchild=s1;
huffTreeRoots[i].rightchild=s2;
huffTreeRoots[i].weight=huffTreeRoots[s1].weight+huffTreeRoots[s2].weight;
}
}
//销毁哈夫曼树
void HuffMan::Destroy() {
len=0;
lnum=0;
delete[] huffTreeRoots;
delete[] huffCode;
}
//哈夫曼编码
void HuffMan::Coding() {
char *cd;
int i,c,f,start;
//求n个叶结点的哈夫曼编码
cd=new char[lnum]; //分配求编码的工作空间
cd[lnum-1]='\0'; //编码结束符
for(i=1;i<=lnum;++i){ //逐个字符求哈夫曼编码
start=lnum-1; //编码结束符位置
for(c=i,f=huffTreeRoots[i].parent;f!=0;c=f,f=huffTreeRoots[f].parent)
{
if(huffTreeRoots[f].leftchild==c){
start--;
cd[start]='0';
}else{
start--;
cd[start]='1';
}
}
//把cd中从start到末尾的编码复制到huffCode中
huffCode[i].assign(&cd[start]);
}
delete[]cd; //释放工作空间
}
int main(){
int t,n,i,j;
int wt[800];
HuffMan myHuff;
cin>>t;
for(i=0;i<t;i++){
cin>>n;
for(j=0;j<n;j++)
cin>>wt[j];
myHuff.MakeTree(n,wt);
myHuff.Coding();
for(j=1;j<=n;j++){
//输出各权值
cout<<myHuff.huffTreeRoots[j].weight<<'-';
//输出各编码
cout<<myHuff.huffCode[j]<<endl;
}
myHuff.Destroy();
}
return 0;
}
我是花花,祝自己也祝您变强了~