使用遞歸算法編寫的費諾編碼

內容與設計思想

按照教材方式建立數據成員變量.設有離散無記憶信源X,P(X).二進制費諾編碼爲:1.將信源符號按概率從大到小的順序排列2.將信源分成兩組――按兩組概率之差爲最小分.3.上面一組編碼爲0,下面一組編碼爲1,一直分到一組只有一個信源爲止.4.將一個信源分組得到的01全部連接起來,組成該信源的碼字,信源即得到自己的費諾編碼.

程序共分爲編碼,排序,解碼三大部分.
首先建立信源的數據存儲類class DATA,設有公共成員6個:char Xi;//信源符號,float PXi;//信源概率,char key[11];//碼字,DATA *next,*qian,*r;//地址.信源輸入採用鏈表,這樣可以輸入任意個信源.
1排序.排序函數DATA* sort(DATA* pp)採用遞歸方式,當遞歸至最後項爲pp->next==NULL時結束.函數對輸入的DATA*型指針進行篩選操作,每次函數運行都從DATA*型鏈表中抽一個最小的出來,當遞歸運行到最後一個DATA*型數據時,所剩就爲最大的數據了,這時開始重組這些數據,依次進行p->qian,p->next的鏈表頭尾地址存入,構造出一個從大到小排列的鏈表列.返回一個指向新鏈表頭的DATA*型指針,函數運行完畢.
2編碼.編碼函數void a(DATA* pp)採用遞歸方式,當遞歸至最後項爲pp->next==NULL時結束.這裏設了4個變量:float y=1; k++;//遞歸自增值,用於字符數組定位,DATA *head1=pp,*head2;int o=1;.用來作函數迭代的內部變量.函數處理方式是對傳入函數的鏈表進行分組,用while循環計算分成上下兩組的概率之差,按兩組概率之差爲最小分組.分好組後,對上面的組賦"0"值,對下面的組賦"1"值,並記錄下這兩個組的頭地址head1head2,分別傳給a函數,a(head1); a(head2);也就編碼函數本身,進行遞歸運算.函數遞歸運算完畢將使每個信源得到自己的編碼,但next指針被破壞了,爲了使鏈表能繼續使用,所以在DATA類中設了r(右)成員.

3解碼.解碼函數void b(DATA* p,char *jie)採用遞歸方式,當遞歸至最後項爲'/0'時結束,即jie[temp]=='/0'.每次函數運行時都定義了2個變量:DATA *temp2=new DATA;DATA *temphead=temp2;//記錄頭地址.並temp++temp爲靜態變量);記錄函數運行的次數.函數每運行一次,對待解碼的字符數組的字符位置就前進一位,便於遞歸時對字符數據的操作.當函數第一次運行,待解碼的第一個字符會有與之相同的碼字信源符號,記錄下所有與之相同的碼字信源符號的地址,組成一個新的鏈表,賦給解碼函數void b(DATA* p,char *jie)本身,遞歸調用.如此查找,記錄,遞歸下去,最終將得到一個地址,此時停止對比,這個地址的碼字值即爲待解碼的信源符號,輸出這個信源符號――if(temp3==1) {cout<<temp2-> qian->Xi; lzy=temp+1;b(head,jie); },一個符號解出.再解碼剩下的數據else b(temphead,jie);,直到待解碼數據對比完畢.這時只要待解碼數據沒有錯誤輸入,也解碼完畢,函數結束.

 

#include "iostream.h"
#include "math.h"
//
class DATA//數據類,採用雙向表
{
public://初始化PXi=1是爲了在排序迭代時方便
DATA(){next=NULL;qian=NULL;r=NULL;PXi=1;key[0]='/0';key[1]='/0';key[2]='/0';key[3]='/0';key[4]='/0';key[5]='/0';key[6]='/0';key[7]='/0';key[8]='/0';key[9]='/0';key[10]='/0';}
char Xi;//信源符號
float PXi;//信源概率
char key[11];//碼字
DATA *next,*qian,*r;//地址
};
DATA *head=new DATA,*p=head;//mainini
int k=(-1);//編碼函數用
void a(DATA* pp);//編碼函數聲明
DATA* sort(DATA* pp);//排序函數聲明
DATA *HEAD=new DATA,*tt=HEAD,*T;//排序函數用
void b(DATA* p,char *jie);//解碼函數聲明
int temp=-1,lzy;//解碼函數用.lzy用來使p->key[]中的數組位置回到第0位
//
void main()
{//輸入數據
float l;
char L;
 while(1)
 {cout<<"輸入信源符號:以*結束輸入"<<endl;//輸入
 cin>>L;
 if(L=='*') break;
 cout<<"輸入信源概率:"<<endl;
 cin>>l;
 p->Xi=L;
 p->PXi=l;
 p->next=new DATA;
 p->next->qian=p;//對新開類賦值
 p->r=p->next;
 p=p->next;
 }
//排序
T=sort(head);//因爲sort要改變tt,故需要一箇中間變量
tt->next=T;//由於迭代產生的鏈表格式不規範,以下句用來整理sort函數的返回結果
tt->next->qian=tt;
tt=tt->next;
tt->next=new DATA;
tt->next->qian=tt;//對新開類賦值
tt=tt->next;
HEAD->next->next->qian=NULL;
HEAD=HEAD->next->next;
cout<<"對輸入信源排序結果如下:"<<endl;
for(p=HEAD;p->next!=NULL;p=p->next)//排序輸出
cout<<"xi:"<<p->Xi<<"pxi:"<<p->PXi<<endl;
//編碼
cout<<"對輸入信源編碼結果如下:"<<endl;
a(HEAD);//編碼
//解碼
char jie[100];//解碼用變量
cout<<"請輸入需解碼數據:"<<endl;
cin>>jie;
b(head,jie);
}
//編碼.k
void a(DATA* pp)//定義遞歸函數
{float y=1;//y定義爲1是因爲概率最多爲1
k++;//遞歸自增值,用於字符數組定位
DATA *head1=pp,*head2;
int o=1;
 while(1)//分01組
 {
 float l=0,z=0;
  for(int i=1;i<=o;i++)
  {
  if(pp->next==NULL) break;
  l=l+pp->PXi;
  pp=pp->next;
  }
 head2=pp->qian;//從這裏分01段
  for(;pp->next!=NULL;pp=pp->next) z=z+pp->PXi;
  if(y>fabs(l-z))//判斷兩組值之差是否最小
  {
  y=fabs(l-z);
  pp=head1;
  o++;
  continue;
  }
  else if(z==0&&i<=2)//z=0i<1表示只有一個概率了
  {cout<<"xi:"<<head1->Xi<<"pxi:"<<head1->key<<endl;
  break;
  }
  for(DATA* u=head1;u->next!=head2->next;u=u->next) u->key[k]='0';//爲字符串賦值
  for(DATA* h=head2;h->next!=NULL;h=h->next) h->key[k]='1';
 head2->qian->next=new DATA;//分段:標記head2爲上一段結束位置
 head2->qian->next->qian=head2->qian;//ini
 a(head1);//遞歸
 a(head2);
 break;
 }
k--;//迭代還原到上一個數組位置
}
//排序.HEAD,tt,T
DATA* sort(DATA* pp)//函數遞歸後頭變到HEAD->next->next.返回值得到最後個head2沒有與tt相連,需另設.得不到結尾爲空的(next=MULL)地址
{
DATA *head1=pp,*head2=pp;
if(pp->next==NULL) return pp;//當pp是最後一個直時
 for(;pp->next!=NULL;pp=pp->next)
 {
 if(1-pp->PXi>=1-head2->PXi) //兩個以上的值時,由於最後一個pxi爲1,所以每次都會有個最小值
 head2=pp; 
 }
 if(head2->qian==NULL)//當pp是第一個直時
 {
 head2->next->qian=NULL;
 head1=head1->next;
 }
 else //當pp是最後一個值及中間的值時
 {head2->qian->next=head2->next;
 head2->next->qian=head2->qian;
 }
tt->next=sort(head1);//遞歸,先得第一個,再得下一個
tt->next->qian=tt;
tt=tt->next;
return head2;
}
//解碼.temp,lzy,head
void b(DATA* p,char *jie)
{DATA *temp2=new DATA;
DATA *temphead=temp2;//記錄頭地址
temp++;
if(jie[temp]=='/0') return;
int temp3=0;
 while(p->r!=NULL)//;do找與解碼數據相同的pxi項,並提取地址到重新構造的鏈表中
 {
  if(p->key[temp-lzy]==jie[temp])
  {
  temp2->Xi=p->Xi;
  int i=(-1);
   do
   {i++;
   temp2->key[i]=p->key[i];
   }while(p->key[i]!='/0');
  temp2->r=new DATA;
  temp2->r->qian=temp2;
  temp2=temp2->r;//對新開類賦值
  temp3++;
  }
 p=p->r;//
 }
 if(temp3==1)
 {cout<<temp2->qian->Xi;
 lzy=temp+1;//回位
 b(head,jie);
 }
else b(temphead,jie);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章