先从简单的Huffman树讲起
我们选用优先队列构造小顶堆,每次从堆中提取最小元素
以一个例子作为引子:
ps:priority_queue<int> q;
直接得到的是大顶堆,每次输出的是队列中最大的元素
我们需要将其改为priority_queue<int,vector<int>,greater<int> > q;
才能改变堆为小顶堆
程序不难,反复输出即可
//注意取值范围
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
priority_queue<long long,vector<long long>,greater<long long> > q;
long long data;//果子个数
int n;//果子种类数
int main(){
while(cin>>n){
for(int i=0;i<n;i++){
cin>>data;
q.push(data);
}
long long a,b,ans=0;
while(q.size()>1){
a=q.top();
q.pop();
b=q.top();
q.pop();
ans=ans+a+b;
q.push(a+b);
}
cout<<ans<<endl;
}
return 0;
}
再看构建一棵huffman树
构建树就必然会带来左右孩子的问题,有左右孩子就会有指针存在。胡凡大神给出的解决方式是分配一个静态的结点数组,每次都分配新结点就从结点数组中取,这样可以保证每个结点的地址不同。
我开始没有采用这种做法,申请左右孩子新结点时,按照以下步骤来进行:
while(true){
node a,b;
father.rchild=a;
father.lchild=b;
}
这里存在这让程序员最为头疼的问题,while中每次node都会指向while内部地址(ps:不知道我理解的对不对)导致每次的新结点都会指向同一个地址从而引发了无穷递归问题。
所以我们采用胡凡大神提供方法,设置静态数组
局部操作如下:
node* creat(){
size++;
return &nodes[size];
}
全部的huffmanCode:
#include<iostream>
#include<queue>
#include<vector>
#include<string>
using namespace std;
//使用静态分配树结点方式
//否则指针地址很难解决
const int maxn=1000;
struct node{
double data;
node *lchild,*rchild;
bool operator > (const node b) const{
return this->data > b.data;
}
}nodes[maxn];
int size=-1;//借助size顺序分配结点
priority_queue<node*,vector<node*>,greater<node*> > q;
node* creat(){
size++;
return &nodes[size];
}
void preorder(node* r){//前序遍历huffman树
if(r!=NULL){
cout<<r->data<<endl;
preorder(r->lchild);
preorder(r->rchild);
}
}
void huffmancode(node *r, string s){
if(r!=NULL){
string s1=s+"0",s2=s+"1";
huffmancode(r->lchild,s1);
huffmancode(r->rchild,s2);
if(r->rchild==NULL&&r->lchild==NULL)
cout<<s<<endl;
}
}
int main(){
int n;
double d;
node* tmp;
cin>>n;
for(int i=0;i<n;i++){
tmp=creat();
cin>>tmp->data;
tmp->lchild=NULL;
tmp->rchild=NULL;
q.push(tmp);
}
while(q.size()>1){
node *a,*b;
node *res=creat();
a=q.top();q.pop();
b=q.top();q.pop();
res->data=a->data+b->data;
res->lchild=a;
res->rchild=b;
q.push(res);
}
tmp=q.top();q.pop();
//preorder(tmp);
huffmancode(tmp,"");
return 0;
}