1. 程式人生 > >計算哈夫曼編碼長度

計算哈夫曼編碼長度

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

#define BUFF_SIZE 4096
#define HASH_SIZE 256

char buff[BUFF_SIZE];        //緩衝區 
int
  hash[HASH_SIZE];        //統計每個字元出現的次數
int heap[(HASH_SIZE<<1)+2];
int pos[HASH_SIZE+1][3];//內部節點 , 0和1記錄子結點位置,3節錄當前的深度
int tlen[HASH_SIZE+1];        //記錄每個葉子結點的深度
int  fd;                      //檔案描述符
int sym_num ;           //檔案中出現的符號數量
int SUM(0);              //檔案中字元總數

//初始化程式
void init(const char * pathname)
{
       memset(buff , 0
 , sizeof(buff));
       memset(hash , 0 , sizeof(hash));
       memset(heap , 0 , sizeof(heap));
       memset(pos  , 0 , sizeof(pos));
       memset(tlen , 0 , sizeof(tlen));
       //開啟檔案
       fd = open(pathname , O_RDONLY);
       if(fd < 0){
              printf("init: %s dont exit!\n" , pathname);
              exit(1
);
       }
}

//統計檔案中每個符號出現的次數
void count_symbol()
{
       lseek(fd , 0 , SEEK_SET);
       while(read(fd , buff , BUFF_SIZE)){
              SUM += strlen(buff);
              for(int i=strlen(buff) - 1;i>=0;i--)
                     hash[(unsigned int)(buff[i] & 0xFF)]++;
       }
       //記錄出現的符號數量;
       for(int i = HASH_SIZE - 1; i >= 0; i--)
              if(hash[i])sym_num++;
}

//建立一個最小堆
void build_min_heap()
{
       for(int i=sym_num;i>0;i--){
              int p = i >> 1 , j = i;
              while(p >= 1){
                     if(heap[heap[p]] > heap[heap[j]])
                            std::swap(heap[j] , heap[p]);
                     j = p; p >>= 1;
              }
       }
}

//每次取出最小數之後重新調整堆,
//h 指推中元素的個數
void heap_adjust(int h)
{
       int t = 1 , p , q , l;
       while(t<h){
              p = t<<1; q = p + 1; l = t;
              if(p <= h && heap[heap[p]] < heap[heap[t]])l = p;
              if(q <= h && heap[heap[q]] < heap[heap[l]])l = q;
              if(l == t)break;
              std::swap(heap[l] , heap[t]);
              t = l;
       }
}
//計算每個字元編碼的長度
void huff_length()
{
       int i , j , p , h , m1 , m2;
       for(i=1 , p=0;i<=sym_num;i++){
              while(!hash[p])       p++;
              heap[sym_num + i] = hash[p];
              heap[i] = sym_num + i;
              p++;
       }
       h = sym_num;
       //對1到n建立最小堆
       build_min_heap();
       while(h>1){
              //取出最小數
              m1 = heap[heap[1]];
              pos[h][0] = heap[1];
              heap[1] = heap[h];
              h--;
              heap_adjust(h);
              //取出次小數
              m2 = heap[heap[1]];
              pos[h+1][1] = heap[1];
              //最後數和次小數之和放在堆的最後一個位置
              heap[h+1] = m1 + m2;
              //重新指向最新合併的結點
              heap[1] = h+1;
              heap_adjust(h);
       }
       //統計編碼長度 , 線性時間統計
       int ts = sym_num << 1;
       for(int i=2;i<=sym_num;i++){
              if(pos[i][0] <= sym_num) pos[pos[i][0]][2] = pos[i][2] + 1;
              else tlen[pos[i][0] - sym_num] = pos[i][2] + 1;
              if(pos[i][1] <= sym_num) pos[pos[i][1]][2] = pos[i][2] + 1;
              else tlen[pos[i][1] - sym_num] = pos[i][2] + 1;
       }
}
int main()
{
       init("data.dat");
       count_symbol();
       huff_length();
       unsigned int sum = 0;
       for(int i=1;i<=sym_num;i++)
              sum += tlen[i] * heap[sym_num + i];
       cout<<SUM <<"\t\t"<<sum<<"\t\t"<<sum*1.0/SUM<<endl;
       return 0;
}