huffman編碼實現

 

大概思路:

        1.計算目標文件A中的字符出現頻率,從而確定權值

        2.由字符權值構建huffman樹,結構體數組作爲此樹的數據結構

        3.由huffman樹將目標文件A中所出現的每個字符編碼

        4.用字符編碼將目標文件編碼到新文件B

        5.將編碼文件B譯碼爲文件C

 

/***************************************************************************
 *   Copyright (C) 2006 by Lingkun         *
 *   [email protected]            *
 *           *
 *   文件名稱:huffman_code.c       *
 *           *
 *   摘 要:利用Huffman編碼目標文件,並完成譯碼                                *
 *              *
 *   使用方法: huffman_code.exe   待編碼文件路徑 編碼文件路徑 譯碼文件路徑 *              
 *           *
 ***************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define   N   1000 

typedef struct { //字符頻率結構體數組
 char  data;
 int  count;
 float  frequent;
} character ;

typedef struct { //huffman樹數組
 char data;
 float weight;
 int  lchild, rchild, parent;
} hufmtree;


typedef char **hufmcode;  //huffman編碼列表

hufmtree *Huffman_tree(int n, character *ch);
hufmcode Huffman_Coding(hufmtree *ht, int n);
character *Frequent_of_character(FILE *fp);
int prn_char(character *ch);
void prn_tree(hufmtree *ht);
FILE *file_open(const char *dir);
void Coding_file(hufmcode hc, const char *dir, const char *dir_code);
void Uncoding_file(hufmtree *ht, const char *dir_code, const char *dir_re, int m);


int main(int argc, char *argv[])
{
 FILE *fp;
 character *ch;
 hufmtree *ht;
 hufmcode hc;
 int m, n;
 
 const char *dir = argv[1];   //待編碼文件路徑
 const char *dir_code = argv[2];  //編碼文件路徑
 const char *dir_re = argv[3];  //譯碼文件路徑
 
 fp = file_open(dir); 
 ch = Frequent_of_character(fp);
 n = prn_char(ch);
 m = 2 * n - 1;
 
 ht = Huffman_tree(n, ch); 
 prn_tree(ht);
 hc = Huffman_Coding(ht, n);

 Coding_file( hc, dir, dir_code);
 Uncoding_file(ht, dir_code, dir_re, m); 
 fclose(fp);
 
 return 0;
}

hufmtree *Huffman_tree(int n, character *ch)
{//按照字符出現頻率構建huffman樹
 hufmtree *ht;
 int   i, j, p1, p2, m;
 float   small1, small2; 
 
 m = 2 * n - 1;  //所有結點數=葉結點*2 - 1
 ht = (hufmtree *)malloc((m + 1) * sizeof(hufmtree)); //huffman樹,(m+1)表示從1開始
 if(ht == NULL) printf("Memory allocate error./n");
 for(i = 1; i <= m ; i++) {     //huffman樹所有結點初始化
  ht[i].data = ch[i].data;    //對應字符賦值
  ht[i].weight = 0.0;     //初始爲0
  ht[i].lchild = ht[i].rchild = ht[i].parent = 0;
 }
 putchar('/n');
 for(i = 1; i <= n; i++){    //葉子結點權值初始化
  ht[i].weight = ch[i].frequent;
 }
 printf("構建哈夫曼樹/n");
 printf("葉子結點個數 n = %d, 總結點個數 m = %d/n", n, m);
 for(i = n + 1; i <= m; i++){  //構建huffman樹
  p1 = 0;
  p2 = 0;
  small1 = small2 = 1;
  for(j = 1; j <= i - 1; j++){   //查找最小,次小元素
   if(ht[j].parent == 0){
    if(ht[j].weight < small1){
     small2 = small1;
     small1 = ht[j].weight;
     p2 = p1;
     p1 = j;
    }
    else if(ht[j].weight < small2){
     small2 = ht[j].weight;
     p2 = j;
    }
   }
  }
  ht[p1].parent = i ;  //最小元素父結點位置賦值
  ht[p2].parent = i ;  //次小元素父結點位置賦值
  ht[i].lchild = p1 ;  //父結點的左孩子爲最小元素
  ht[i].rchild = p2 ;  //父結點的右孩子爲次小元素
  ht[i].weight = ht[p1].weight + ht[p2].weight; //父結點權值=次小權值+最小權值
 }
 return ht;
}

hufmcode Huffman_Coding(hufmtree *ht, int n)
{//huffman編碼
 int   i, c, start, f;
 hufmcode hc;
 char *cd;
 
 hc = (hufmcode)malloc((n + 1) * sizeof(char *)); //編碼列表初始化
 cd = (char *)malloc(n * sizeof(char));   //臨時編碼列表
 cd[n - 1] = '/0';
 printf("/n將以上字符進行哈夫曼編碼:/n");
 for(i = 1; i <= n; i++){    //將字符進行哈夫曼編碼
  start = n - 1;
  for(c = i, f = ht[i].parent; f != 0; c = f, f = ht[f ].parent){
   if(ht[f].lchild == c)  { cd[--start] = '0'; }
   else { cd[--start] = '1'; }
  } 
  hc[i] = (char *)malloc((n - start) * sizeof(char));  
  strcpy( &hc[i][1], &cd[start]);  //將臨時編碼複製到編碼列表相應位置
  hc[i][0] = ht[i].data;    //編碼列表首位置存儲字符
 } 
 putchar('/n');
 for(i = 1; i <= n; i++){
  printf("hc[%d]:  %c    %s/n", i, hc[i][0], &hc[i][1]);
 }
 return hc;
}

character *Frequent_of_character(FILE *fp)
{//計算字符出現頻率
 int i, last, length = 0;
 character *ch;
 char c;
 
 ch = (character *)malloc( N * sizeof(character));
 printf("/n/n打印原文:/n");
 while( (c = fgetc(fp)) != EOF) {
  printf("%c", c);
  length++;
 }
 printf("/n/n原文件長度:/n");
 printf("/nlength = %d/n/n", length);
 
 fseek(fp, 0, SEEK_SET);
 last = 0;    //當前字符種類數
 while((c = fgetc(fp)) != EOF) {
  i = 0;  
  while( c != ch[i].data && i <= last && last <= N) { //判斷是否有新字符出現OR字符種類是否大於N(溢出)
   i++;
  }
  if(i > last){  //出現新字符
   last = i;
   ch[last].data = c;
   ch[last].count++;
  }
  else if( c == ch[i].data){ //已出現字符
   ch[i].count++;
  }
  else {   //字符種類大於N
   printf("字符種類大於N./n");
  }
 }
 for(i = 1; ch[i].count != 0; i++){  //計算每個字符出現頻率,作爲權值
  ch[i].frequent = (float)ch[i].count / length;
 }
 return ch;
}

int prn_char(character *ch)
{//打印字符出現情況
 int i, count;
 
 printf("字符個數及其出現頻率:/n");
 printf("序列號  字符  計數  頻率/n");
 for(i = 1; ch[i].count != 0; i++){
  printf("ch[%d] :   %c    %d    %f ./n", i, ch[i].data, ch[i].count, ch[i].frequent);
 }
 count = i - 1;
 return count;
}
 
FILE *file_open(const char *dir)
{//文件打開函數
 FILE *fp;
 
 fp = fopen( dir, "r" );
 if(fp == NULL) printf("File %s open Error/n", dir);
 return fp;
}

void prn_tree(hufmtree *ht)
{//打印huffman樹
 int i;
 
 printf("/n打印哈夫曼樹:/n");
 printf("序列號  字符   權值        左孩子        右孩子      雙親/n");
 for(i = 1; (ht[i].parent != 0 || ht[i].lchild != 0) && ht[i].weight != 0.0; i++) {
  printf("ht[%d] :  %c    %f     lchild: %d    rchild: %d   parent: %d/n", i, ht[i].data, ht[i].weight, ht[i].lchild,  ht[i].rchild, ht[i].parent );
 }
}

void Coding_file(hufmcode hc, const char *dir, const char *dir_code)
{//將文件dir編碼
 int i;
 FILE *fp1, *fp2;
 char c;
 
 fp1 = file_open( dir);
 fp2 = fopen( dir_code, "a+" );
 if(fp2 == NULL) printf("File %s open ERROR./n", dir_code);
 
 printf("將原文件編碼:/n");
 while((c = fgetc(fp1)) != EOF) { 
  for( i = 1; hc[i][0] != c; i++) ;  //在編碼列表中查找字符
  fputs( &hc[i][1], fp2);    //將字符對應的編碼寫入fp2指向的文件
  printf("%s", &hc[i][1]);
 }
 fclose(fp1);
 fclose(fp2);
}

void Uncoding_file(hufmtree *ht, const char *dir_code, const char *dir_re, int m)
{//將已編碼文件譯碼
 int i;
 char c;
 FILE *fp1, *fp2;
 
 i = m;   //從樹根開始
 fp1 = file_open( dir_code);  //已編碼文件
 fp2 = fopen( dir_re , "a+"); //譯碼後文件
 if(fp2 == NULL) printf("File %s open error./n", dir_re);
 
 c = fgetc(fp1);   //讀取一個編碼字符
 printf("/n將編碼文件翻譯成明文:/n");
 while(c != EOF) {  //將編碼文件翻譯成明文
  if(c == '0') {
   i = ht[i].lchild ;
  }
  else if(c == '1') {
   i = ht[i].rchild ;
  }
  else printf("Uncoding ERROR./n");
  if(ht[i].lchild == 0) {  //走到葉子結點,將其對應字符寫入fp2指向的文件
   putchar(ht[i].data);
   fputc( ht[i].data, fp2);
   i = m;   //重新從樹根開始
  }
  c = fgetc(fp1);
 }
 fclose(fp1);
 fclose(fp2);
}

 

這是上學期數構課的學習成果,現在翻出來回顧一下,還是頗覆雜的,呵呵
 

發佈了25 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章