程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> poj 1521 Entropy huffman(哈夫曼)編碼

poj 1521 Entropy huffman(哈夫曼)編碼

編輯:C++入門知識

題目很長,長的都讀不懂咋回事,不過很好理解,意思就是給你個字符串,讓你輸出用普通ASCII編碼和用huffman編碼分別占用的位數,然後輸出壓縮比;

第一次寫哈夫曼編碼,寫了半天,到最後還wa了好幾次,這個壓縮比的輸出格式太蛋疼了,他說保留一位小數,但是如果是x.0,則直接輸出x,這一天找了半天,最後試了好些格式才給試出來的。雖然題目沒有要求寫編碼和解碼,但是由於想練習下,我自己也給寫了出來;

 


大題思路是:首先找出每個字符出現的頻率(即次數),然後每個字符首先占一個節點,然後對所有節點取頻率最小的兩個,再構造一個節點為這兩個節點的父節點,其頻率為這個節點頻率之和,直到只剩下一個節點,此節點即為根節點,然後對各個節點進行編碼,跟節點沒有編碼,然後遞歸編碼,規則為:一個節點的左孩子的編碼為此節點的編碼加‘0’,右孩子的編碼為此節點的編碼+‘1’;直到沒有左右孩子為止;

 


然後求編碼長度的時候,對於所有的葉子節點,讓其編碼長度乘頻率,然後累加,就得到所有編碼的長度了。

參考代碼如下:


[cpp]  #include <iostream>  
#include <iomanip>  
#include <cstdlib>  
#include <cstring>  
#include <queue>  
#include <cmath>  
#include <cstdio>  
using namespace std; 
struct Node 

    int val;   //記錄節點的頻率  
    char c;    //節點機率的字符,中間節點的c值均設為0;  
    int len;    //編碼的長度;  
    char code[31]; //編碼;  
    int num;   //記錄在數組中的位置,存屬為了操作方便,完全可以沒有;  
    int lchild; //左孩子的下標 若沒有 為-1;  
    int rchild; //右孩子的下標,若沒有,為-1;  
    int parent; //父親節點的下標,若沒有,為-1;  
}; 
bool operator <(const Node &a,const Node &b)  //考慮到用到優先隊列取最小和次小,所有重載下<號  

    return a.val>b.val; 

Node a[201]; 
int lens;    //字符串長度  
int lentree;  //數的節點的個數;  
char s[10001]; //讀入的字符串  
int flag[10001];  //用來判斷字符串的某一位的字符之前有沒有出現過  
int pcodelen,nowcodelen;  //分別表示用ASCII編碼和用huffman編碼的長度  
double ans;     //壓縮比  
void codenode(int k)  //對各個節點進行編碼;  

    if(a[k].lchild!=-1) 
    { 
        strcpy(a[a[k].lchild].code,a[k].code); 
        a[a[k].lchild].code[a[k].len]='0'; 
        a[a[k].lchild].len=a[k].len+1; 
        codenode(a[k].lchild); 
    } 
    if(a[k].rchild!=-1) 
    { 
        strcpy(a[a[k].rchild].code,a[k].code); 
        a[a[k].rchild].code[a[k].len]='1'; 
        a[a[k].rchild].len=a[k].len+1; 
        codenode(a[k].rchild); 
    } 
    if(a[k].lchild==-1&&a[k].rchild==-1) 
    { 
        nowcodelen+=a[k].val*a[k].len;  //如果為葉子幾點則令nowcodelen的值增加此節點編碼長度的值  
    } 
 

void codestr(char *source,char *code) //對字符串進行編碼  

    int i; 
    int j; 
    int lencode=0; 
    int lensource=strlen(source); 
    for(i=0;i<lensource;i++) 
    { 
        for(j=0;j<(lentree+1)/2;j++) 
        { 
            if(a[j].c==source[i]) 
            { 
                strcpy(code+lencode,a[j].code); 
                lencode+=a[j].len; 
            } 
        } 
    } 
    code[lencode]=0; 

void decode(char *code,char *str)//解碼  

    int i; 
    int lenstr=0; 
    int lencode=strlen(code); 
    for(i=0;i<lencode;) 
    { 
        int t=lentree-1; 
        while(a[t].lchild!=-1||a[t].rchild!=-1) 
        { 
            if(code[i]=='0') 
            { 
                t=a[t].lchild; 
                i++; 
            } 
            else 
            { 
                t=a[t].rchild; 
                i++; 
            } 
        } 
        str[lenstr++]=a[t].c; 
    } 
    str[lenstr]=0; 

void execute() 

    if(lentree==1) 
    { 
        nowcodelen=a[lentree-1].val; 
        return ; 
    } 
    int i; 
    priority_queue<Node> q; 
    while(!q.empty()) 
        q.pop(); 
    for(i=0;i<lentree;i++) 
    { 
        a[i].rchild=a[i].lchild=-1; 
        a[i].num=i; 
        q.push(a[i]); 
    } 
    while(q.size()!=1)//構造最優二叉樹  
    { 
        Node x1=q.top(); 
        q.pop(); 
        Node x2=q.top(); 
        q.pop(); 
        a[lentree].val=x1.val+x2.val; 
        a[lentree].rchild=x1.num; 
        a[lentree].lchild=x2.num; 
        a[lentree].num=lentree; 
        q.push(a[lentree]); 
        lentree++; 
    } 
    a[lentree].len=0; //先令根節點的編碼長度為0;  
    nowcodelen=0; 
    codenode(lentree-1); 
 
    /*
    char code[10001];
    codestr(s,code);
    cout<<"編碼為:"<<endl;
    cout<<code<<endl;
    char str[10001];
    decode(code,str);
    cout<<"解碼後字符串為:"<<endl;
    cout<<str<<endl;
    */ 

int main() 

    char c; 
    char cmp[6]={'E','N','D','\0'}; 
    while(gets(s)&&strcmp(s,cmp)!=0) 
    {    
        lens=strlen(s); 
        lentree=0; 
        memset(flag,-1,sizeof(flag)); 
        memset(a,0,sizeof(a)); 
        for(int i=0;i<lens;i++)//構造所有葉子節點  
        { 
            if(flag[i]!=-1) 
                a[flag[i]].val++; 
            else 
            { 
                a[lentree].c=s[i]; 
                a[lentree].val++; 
                for(int j=i+1;j<lens;j++) 
                    if(s[j]==s[i]) 
                        flag[j]=lentree; 
                 
                lentree++; 
            } 
        } 
        pcodelen=8*lens; //ASCII編碼長度就為字符串長度乘8  
        execute(); 
        ans=(double)pcodelen/(double)nowcodelen;  
        cout<<pcodelen<<' '; 
        cout<<nowcodelen<<' '; 
        if((ans-floor(ans))<0.1) //如果ans小數點後一位為0,則輸出個整數  
            cout<<ans<<endl; 
        else 
            cout<<fixed<<setprecision(1)<<ans<<endl; 
    } 
    return 0; 

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <cmath>
#include <cstdio>
using namespace std;
struct Node
{
 int val;   //記錄節點的頻率
 char c;    //節點機率的字符,中間節點的c值均設為0;
 int len; //編碼的長度;
 char code[31]; //編碼;
 int num;   //記錄在數組中的位置,存屬為了操作方便,完全可以沒有;
 int lchild; //左孩子的下標 若沒有 為-1;
 int rchild; //右孩子的下標,若沒有,為-1;
 int parent; //父親節點的下標,若沒有,為-1;
};
bool operator <(const Node &a,const Node &b)  //考慮到用到優先隊列取最小和次小,所有重載下<號
{
 return a.val>b.val;
}
Node a[201];
int lens;    //字符串長度
int lentree;  //數的節點的個數;
char s[10001]; //讀入的字符串
int flag[10001];  //用來判斷字符串的某一位的字符之前有沒有出現過
int pcodelen,nowcodelen;  //分別表示用ASCII編碼和用huffman編碼的長度
double ans;     //壓縮比
void codenode(int k)  //對各個節點進行編碼;
{
 if(a[k].lchild!=-1)
 {
  strcpy(a[a[k].lchild].code,a[k].code);
  a[a[k].lchild].code[a[k].len]='0';
  a[a[k].lchild].len=a[k].len+1;
  codenode(a[k].lchild);
 }
 if(a[k].rchild!=-1)
 {
  strcpy(a[a[k].rchild].code,a[k].code);
  a[a[k].rchild].code[a[k].len]='1';
  a[a[k].rchild].len=a[k].len+1;
  codenode(a[k].rchild);
 }
 if(a[k].lchild==-1&&a[k].rchild==-1)
 {
  nowcodelen+=a[k].val*a[k].len;  //如果為葉子幾點則令nowcodelen的值增加此節點編碼長度的值
 }

}
void codestr(char *source,char *code) //對字符串進行編碼
{
 int i;
 int j;
 int lencode=0;
 int lensource=strlen(source);
 for(i=0;i<lensource;i++)
 {
  for(j=0;j<(lentree+1)/2;j++)
  {
   if(a[j].c==source[i])
   {
    strcpy(code+lencode,a[j].code);
    lencode+=a[j].len;
   }
  }
 }
 code[lencode]=0;
}
void decode(char *code,char *str)//解碼
{
 int i;
 int lenstr=0;
 int lencode=strlen(code);
 for(i=0;i<lencode;)
 {
  int t=lentree-1;
  while(a[t].lchild!=-1||a[t].rchild!=-1)
  {
   if(code[i]=='0')
   {
    t=a[t].lchild;
    i++;
   }
   else
   {
    t=a[t].rchild;
    i++;
   }
  }
  str[lenstr++]=a[t].c;
 }
 str[lenstr]=0;
}
void execute()
{
 if(lentree==1)
 {
  nowcodelen=a[lentree-1].val;
  return ;
 }
 int i;
 priority_queue<Node> q;
 while(!q.empty())
  q.pop();
 for(i=0;i<lentree;i++)
 {
  a[i].rchild=a[i].lchild=-1;
  a[i].num=i;
  q.push(a[i]);
 }
 while(q.size()!=1)//構造最優二叉樹
 {
  Node x1=q.top();
  q.pop();
  Node x2=q.top();
  q.pop();
  a[lentree].val=x1.val+x2.val;
  a[lentree].rchild=x1.num;
  a[lentree].lchild=x2.num;
  a[lentree].num=lentree;
  q.push(a[lentree]);
  lentree++;
 }
 a[lentree].len=0; //先令根節點的編碼長度為0;
 nowcodelen=0;
 codenode(lentree-1);

 /*
 char code[10001];
 codestr(s,code);
 cout<<"編碼為:"<<endl;
 cout<<code<<endl;
 char str[10001];
 decode(code,str);
 cout<<"解碼後字符串為:"<<endl;
 cout<<str<<endl;
 */
}
int main()
{
 char c;
 char cmp[6]={'E','N','D','\0'};
 while(gets(s)&&strcmp(s,cmp)!=0)
 { 
  lens=strlen(s);
  lentree=0;
  memset(flag,-1,sizeof(flag));
  memset(a,0,sizeof(a));
  for(int i=0;i<lens;i++)//構造所有葉子節點
  {
   if(flag[i]!=-1)
    a[flag[i]].val++;
   else
   {
    a[lentree].c=s[i];
    a[lentree].val++;
    for(int j=i+1;j<lens;j++)
     if(s[j]==s[i])
      flag[j]=lentree;
    
    lentree++;
   }
  }
  pcodelen=8*lens; //ASCII編碼長度就為字符串長度乘8
  execute();
  ans=(double)pcodelen/(double)nowcodelen;
  cout<<pcodelen<<' ';
  cout<<nowcodelen<<' ';
  if((ans-floor(ans))<0.1) //如果ans小數點後一位為0,則輸出個整數
   cout<<ans<<endl;
  else
   cout<<fixed<<setprecision(1)<<ans<<endl;
 }
 return 0;
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved