數據結構實驗報告

學識都 人氣:2.24W

 

數據結構實驗報告

題目與內容
 哈夫曼(Huffman)樹與哈夫曼碼
 1.輸入一個文本,統計各字符出現的頻度,輸出結果;
 2.使用二叉鏈表或三叉鏈表作存儲結構,構造哈夫曼(Huffman)樹;
 3.確定和輸出各字符的哈夫曼碼;
       4.輸入一個由0和1組成的代碼序列,翻譯並輸出與之對應的文本;
       
二、  數據結構及存儲結構
在這個程序中我用了三叉鏈表tree作爲哈夫曼樹的結構:左、右兒子和父親
節點;並且在開始,我還用此結構生成了單鏈表,用來存儲讀取的字符。編碼的時候,我把編碼放在棧結構stack中,然後逆序輸出即爲哈夫曼編碼。存放葉節點時用到了指針數組。
struct tree(){
 char data;
 int m,sign;
 struct tree *lchild,*rchild,*parent;
}
struct stack{
 int data;
 struct stack *next;
}

算法設計思想
先調用frequency函數讀取字符,創建鏈表來存儲字符及其相關信息;同時把字符放進數組中進行備份,因爲後面編碼時要用到這些字符(它們就是葉節點)。然後遍歷這個鏈表輸出個字符的頻度。接着調用ctree函數來生成哈夫曼樹。在ctree函數中,首先對剛纔那個鏈表按照頻度排序,變成頻度遞增鏈表;然後取其前兩個節點作爲新建哈夫曼樹的左右兒子(注:左兒子的頻度<=右兒子的'頻度),再把它們從鏈表中刪除,並且把新建的哈夫曼樹的根結點插入到鏈表中。這樣重複操作,就生成了哈夫曼樹。然後調用ccode函數編碼。我採用的是從葉到根的編碼方式。先從數組中取出數據(即爲一個葉節點),看其m的值(0/1),放進stack棧中,然後向根遍歷,接着把棧中的數據取出輸出,即爲編碼。最後調用translate函數進行譯碼。先讀取01序列放進新創建的一個鏈表(隊列形式)中,然後從根到葉進行遍歷:從鏈表中取出一個數據,是0則到左子樹,1則到右子樹,如果其左右子樹爲空,則輸出字符data,再取下一個數據從根重新遍歷。這樣就得到譯碼了。


心得體會
這次編程,從開始編到測試成功,我一共花了四天。這主要是因爲之前我花了不少時間看書,把數據結構和存儲結構都想好了,還看了大量程序,不管是相關還是不相關的。例如,有一個困擾我很久的問題:當詢問是否繼續時,輸入y就繼續,否則跳出;以前用getchar要等按了回車才進行判斷,如果按了好幾個y,則後面幾個y被當成以後的輸入處理了,這樣就會出錯。然而我在一個程序中看到了getche這個指令解決了這個問題,它不需等回車就進行處理。另外在定義哈夫曼樹結構時,我加了個sign變量來標誌它是左子樹還是右子樹,這樣後面編碼時就很方便。這次編程使我認識到:要重視細節,雖然很小,但是它會使程序不能運行或出錯。這個程序中我由於把‘y’寫成y,結果浪費了我半天的時間去查錯。

五、  程序清單
#include <stdio.h>
#include <conio.h>
struct tree{         /*定義哈夫曼樹的結構*/
 char data;            /*存放字符*/
 int m,sign; /*m存放字符出現的頻率 sign是左(0)或右(1)兒子的標誌*/
 struct tree *lchild,*rchild,*parent;    /*左兒子 右兒子 父節點*/
};
struct stack{           /*定義棧的結構*/
 int data;
 struct stack *next;
};
struct tree * ps[50],*root,*head;
 /*數組存放字符 root爲二叉樹的根結點 head爲鏈表的頭節點*/
int size;            /*標誌字符個數*/
/*************************統計各字符出現的頻度***********************/
void frequency(){
 struct tree *r,*p,*q;  
 int n,l,j=1;
 /*錄入第一個字符並創建鏈表*/
 clrscr();             /*清屏*/
 head=NULL;
 printf("Input the text end of ENTER:n");
 n=getchar();
 if(n!='n'){
  p=(struct tree*)malloc(sizeof(struct tree));
  p->data=n;
  p->m=1;
  p->sign=-1;
  p->lchild=NULL;
  p->rchild=NULL;
  p->parent=NULL;
  head=p;
  ps[0]=p;     /*把字符(後面的葉節點)放進數組備份*/
  n=getchar();

 }
 /*錄入其它字符*/
 while(n!='n'){
  l=0;r=p;
  for(q=head;q!=NULL&&l==0;q=q->parent){
   if(q->data==n) {    /*檢查是否和已經錄入的字符相同*/
    q->m++;    /*如果相同則此字符的頻度變量加1*/
    l=1;
   }
   r=p;
  }
  if(l==0){      /*如果不同則錄入並加入鏈表*/
   p=(struct tree*)malloc(sizeof(struct tree));
   p->data=n;
   p->m=1;
   p->sign=-1;
   p->lchild=NULL;
   p->rchild=NULL;
   p->parent=NULL;
   r->parent=p;
   ps[j]=p;     /*把字符(後面的葉節點)放進數組備份*/
   j++;
  }
  n=getchar();
 }
 /*輸出字符的頻度*/
 p=head;
 size=0;
 printf("nFrequency as follows:ncharacters frequencyn");
 while(p!=NULL){
  printf("%c  %dn",p->data,p->m);
  p=p->parent;
  size++;         /*統計字符的個數*/
 }
}
/****************************生成樹**********************************/
void ctree(){ 
 struct tree *t,*r,*p,*e,*q;
 int n;
 /****給鏈表排序生成頻度遞增鏈表*****/
   for(p=head;p->parent!=NULL;p=p->parent){
      for(q=p->parent;q!=NULL;q=q->parent){
            if(p->m>q->m){
             n=q->m;       /*交換信息*/

     q->m=p->m;
              p->m=n;
    n=q->data;
    q->data=p->data;
    p->data=n;
         }
   }
 }
 /***********生成哈夫曼樹***********/
 p=head;
 while(p!=NULL&&p->parent!=NULL){
 /*取遞增鏈表前兩個爲左右兒子生成哈夫曼樹*/
  q=p->parent->parent;    /*然後把它們從鏈表中刪掉*/
  t=(struct tree*)malloc(sizeof(struct tree));
  t->lchild=p;       /*頻度:左兒子<=右兒子*/
  t->rchild=p->parent;
  t->m=p->m+p->parent->m;
  t->rchild->parent=t;
  t->rchild->sign=1;
  t->lchild->parent=t;
  t->lchild->sign=0;
  t->sign=-1;
  root=t;          /*root爲根結點*/
  root->parent=NULL;
  if(q!=NULL){        /*判斷鏈表是否爲空*/
   head=q;  
   r=head;
   e=head;     /*把新生成的根結點插入到鏈表中去*/
   if(head->m>t->m){      /*判斷是否爲頭節點*/
    t->parent=q;
    head=t;
   }
   else{
    r=r->parent;
    while(r!=NULL&&r->m<t->m){
     e=r;
     r=r->parent;
    }
    t->parent=r;
    e->parent=t;
   }
   p=head;
   root=t;
  }

  else break;        /*如果鏈表爲空則結束*/
 }
}
/******************************編碼******************************/
void ccode(){      
 struct tree *p,*q;
 int j; 
 printf("ncodes as follows:ncharacters coden");
 for(j=0;j<size;j++){      /*做size(葉節點個數)次循環*/
  head=NULL;
  p=ps[j];          /*從葉到根求編碼*/
  printf("%c  ",p->data);
  while(p->parent!=NULL){       /*把編碼存入棧中*/
   q=(struct stack *)malloc(sizeof(struct stack));
   q->data=p->sign;
   q->next=head;
   head=q;
   p=p->parent;
  }
  q=head;           /*輸出編碼*/
  while(q!=NULL){
   printf("%d",q->data);
   q=q->next;
  }
  printf("n");
 }
}
/******************************譯碼******************************/
char translate(){
 struct tree *r,*p,*q;
 int n;
 char c;
 /*讀取01序列*/
Error: printf("nInput codes consist of 0 and 1 (end of ENTER):n");
 n=getchar();
 if(n!='n'){          /*讀取第一個字符*/
  p=(struct tree*)malloc(sizeof(struct tree));
  p->data=n;
  p->next=NULL;
  head=p;
  r=head;
  n=getchar();
 }
 while(n!='n'){          /*讀取其它字符*/

  p=(struct tree*)malloc(sizeof(struct tree));
  p->data=n;
  p->next=NULL;
  r->next=p;
  r=p;
  n=getchar();
 }
 p=head;
 while(p!=NULL){        /*判斷是否右非法字符*/
  if(p->data!='0'&&p->data!='1'){
   printf("There are illeage characters in your codes!n");
   goto Error;
  }
  p=p->next;
 }
 printf("nThe text of the codes is:");
 p=head;
 q=root;
 while(p!=NULL){         /*由根到葉遍歷*/
  if(q->lchild==NULL&&q->rchild==NULL){  /*判斷是否葉節點*/
   putchar(q->data);
   q=root;
  }
  else {           /*往下遍歷*/
   if(p->data=='0') q=q->lchild;
   else q=q->rchild;
   if(q->lchild==NULL&&q->rchild==NULL){
    putchar(q->data);
    q=root;
   }
  }
  p=p->next;
 }
 printf("nnInput codes again(y/n)?");     /*詢問是否繼續譯碼*/
 c=getche();
 printf("nn");
 return(c);          /*返回是否繼續的標誌*/
}
/******************************主程序******************************/
void main(){
 char c,a;
 
 do{
  frequency();

  ctree();
  ccode();
  c=translate();      /*translate子函數返回值賦給c*/
  for(;c=='y'||c=='Y';){      /*判斷是否繼續譯碼*/
   c=translate();
  }
  printf("nInput text again(y/n)?");
  a=getche();
 }
 while(a=='y'||a=='Y');        /*判斷是否循環*/
 clrscr();
 getchar();
}