`
enefry
  • 浏览: 36667 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

哈弗曼编码/译码器

阅读更多

在离散数学中就看到了哈弗曼编码这个东西....但是当时没有比较具体的思考....当数据结构到来时才选择了这个课程设计...(采用 java 实现)
    实现这个程序有几个核心(实现算法方法有多种):
        1 : 建树
        2 : 查找数据节点(分编码、译码两种)
        3 : 将编码用二进制数据写入
    这个我总共修改了 n 遍, 第一版本是采用 char (java 的char是 16位 ) 是为了压缩文本数据.但是这个的速度确实有点慢(因为全部都是无序链表形式),但是文本压缩率约 58%,之后为了可以压缩所有文件采用了 byte (压缩率有所下降) 进行压缩.这样也减少了内存使用后来使用.后来进行了新的统计查找算法(采用哈希表),极大提高了压缩执行速度..
    压缩char 的程序做了比较美观的界面,可以显示文本内容,界面比较完善.(课程设计嘛).byte 的界面十分简单 ,只是为了实现我的想法.提高可应用性.但是一开始采用的还是链表的方法,执行速度还是有点慢.暑假回家后没事就顺便修改了一下.某天突然来了灵感就改成哈希表形式(压缩速度大大提高).但是这个已经不可能再作为课程设计上交了(小小郁闷一下) ...后来也完善了大文件(2G内)的压缩算法(其实没什么.这里不介绍,下面是界面)


/* 先介绍一下节点类*/
class ByteHfmNode {
    byte data;    int weight;    long code;    int deep;    ByteHfmNode rchild;    ByteHfmNode lchild;    ByteHfmNode next;    private boolean isleaf;
     ByteHfmNode();                               // 无参数构造器   由于构造 树的根
     ByteHfmNode(byte);                           //通过 数据        构造节点
     ByteHfmNode(byte,int);                       //通过 数据和其权  构造叶子节点
     ByteHfmNdoe(ByteHfmNode,ByteHfmNode);        //通过两个孩子节点 构造新节点
     boolean isLeaf();                            // 检验叶子节点
     boolean isRoot();                            // 检验根节点
}
/*********************** 在介绍主要实现功能的 方法*************************************/
/************************  1: 用于统计字符的方法:************************************/
    /**
     * 编码过程添加数据记录(用于统计)
     * @param data      字符
     */
    void append(byte data){
        pos = data+128;      
        if(appendArray[pos] == null){
            appendArray[pos] = new ByteHfmNode(data);
            this.LEAFS ++;
        }else{
            appendArray[pos].weight ++;
        }
    }
    /**
     * 解码过程添加数据(用于统计)
     * @param data      字符
     * @param weight    权
     */
    void append(byte data,int weight){
        unCodeappendTmp.next = new ByteHfmNode(data,weight);
        unCodeappendTmp = unCodeappendTmp.next;
    }
  /*********************** 2 用于建树的方法   ***************************/
/**
     * 建立哈弗曼编码树
     * @throws Exception
     */
    void createTree()throws Exception {
        /*根据编码译码进行建树前初始化*/
        if(Coding){
            this.initSortArray();       //更加原来的统计数组生成 可以排序的数据
            this.sort();               // 排序
            this.arrrayToLink();        //将数组转换为链表(这样方便于建树)
        }else{
            //译码不需要进行初始化;
        }
        ByteHfmNode tmp = head;
ByteHfmNode stmp = head;
while(head.next.next != null){
  stmp = head;
  tmp = new ByteHfmNode(head.next,head.next.next);
  head.next = head.next.next.next;
  while(stmp.next != null&&stmp.next.weight < tmp.weight){
   stmp = stmp.next;
  }
  if(stmp.next == null){
   stmp.next = tmp;
    //stmp = head;
  }else{
                 tmp.next = stmp.next;
   stmp.next = tmp;
  }
}
head.rchild = head.lchild = head.next;
        /* 解码时候需要先进行初始化搜索缓存节点*/
        if(!Coding){
           searchTmp = head.lchild;
        }
    }
/******  建树使用到的方法  *****/
    /**
     * 对编码树进行编码
     * @throws Exception
     */
    void codeTree()throws Exception{
        if(head.lchild.isRoot()){
            this.CODETREE(head.lchild, 0, 0);
        }else{
            CODETREE(head.lchild,1,1);
        }
            
    }
    /**
     * 对哈弗曼数组进行排序(按权值升序排序)
     */
    private void sort(){
        int i=0,j=0;
        int length = sortArray.length;
  ByteHfmNode temp;
  for(int gap = length>>1; gap>0;gap = gap==2?1:(int )(gap/2.2)){
                  for( i = gap;i<length;i++){
                     temp = sortArray[i];
                     j=i;
                     for(;j>= gap && temp.weight < sortArray[j - gap].weight ;j-= gap){
                         sortArray[j] = sortArray[j - gap];
                     }
                     sortArray[j] = temp;
                  }
                }
                        for( i=0;i<sortArray.length;i++){
          
        }
    }
    /**
     * 将排好序的数组转换为链表同时记录下字符及其权值
     */
    private void arrrayToLink(){
        head.next = sortArray[0];
        datas = null;
        weights = null;
        datas = new byte[sortArray.length];
        weights = new int[sortArray.length];
        datas[0] = sortArray[0].data;
        weights[0] = sortArray[0].weight;
        //System.out.println("Length:"+sortArray.length);
/******** 现在需要先将排序号的数据保存下来 这样解码才有数据  ****/
        for(int i = sortArray.length - 1;i>0;i--){
            sortArray[ i-1 ].next = sortArray[i];
            datas[i] = sortArray[i].data;
            weights[i] = sortArray[i].weight;
        }
    }



/******************************** 移位写入文件 ****************************************/
/**                    在这里必须先介绍我的文件结构:
     * 文件结构:
     *      文件头:  ‘B’0xff    2 byte
     *      文件长度: count       4 byte
     *      字符种类: data.length 1 byte
     *      最大权值: weightSize  1 byte
     *      间隔符  : 0xff 0xff   2 byte
     *      权表:{ 字符,对应的权,下一字符,对应权 ..... }
     *      间隔符 : 0xff 0xff   2 byte
     *      编码数据: 二进制文件
     */
public byte[] getHuffmanByteArray()throws Exception{
        if (lastCount == count) {
                return hfmOutput.toByteArray();
            }
            int lastSize = 0;
            if(hfmOutput!=null){
                lastSize = hfmOutput.size();
            }
           
            hfmOutput = null;
            hfmOutput = new ByteArrayOutputStream();
            //******************************************************************
            bhm.createTree();
            bhm.codeTree();
            byte [] datas = bhm.getDatas();
            int  [] weights = bhm.getWeight();
            /* 文件头标志位 byte 哈弗曼编码文件 */
            byte[] fileHead = {'B',(byte)0xff};
            /* 文件内部间隔符 */
            byte[] mark = {(byte)0xff,(byte)0xff};
            int weightSize = weights[weights.length-1];
            /* 根据本次编码最大权值占用 byte 数控制写入权值的位数 */
            if(weightSize<0xff){
                weightSize  = 1;
            }else{
                if(weightSize<0xffff){
                    weightSize = 2;
                }else{
                    if(weightSize<0xffffff){
                        weightSize = 3;
                    }else{
                        weightSize = 4;
                    }
                }
            }
          
            hfmOutput.write(fileHead);
            hfmOutput.write(intToBytes(count));
            hfmOutput.write(datas.length);
            hfmOutput.write(weightSize);
            hfmOutput.write(mark);
            byte wtmp[] = new byte[4];
            for(int i = 0 ;i<datas.length;i++){
                hfmOutput.write(datas[i]);
                wtmp = intToBytes(weights[i]);
                hfmOutput.write(wtmp,0,weightSize);
            }
            hfmOutput.write(mark);
            /* 权表保存完毕,下面写入数据*/
            /* 临时缓存变量*/
            byte ct =0;
            /* 记录上次写入剩余数据*/
            byte buffer = 0;
            /* 记录本次写入开始位*/
            int nowPos = 0;
            /* 记录本字符编码*/
            long code = 0;
            /* 记录本字符编码长度*/
            int deep = 0;
            /* 计数器*/
            int k = 0 ;
            ByteHfmNode hfmTmp;
           // System.out.println(count);
            int bit =0;
            int i = 0;
            int writeTime = 0;
            for( i=0;i<count;i++){
                hfmTmp = bhm.getNode(buf[i]);
                code = hfmTmp.code;
                deep = hfmTmp.deep;
                bit+=deep;
                if(nowPos+deep>8){
                /* 编码已经超过一个字节 先写入*/
                ct = (byte)((code>>(deep - ( 8 - nowPos)))&0xff);
                ct = (byte)((ct)|(buffer));
                hfmOutput.write(ct);
                writeTime++;
                for(k = 1; deep -(k<<3) + nowPos >8; ){
                    ct = (byte) ( (code>>( deep - ((++k)<<3) +nowPos ) )& 0xff );
                    hfmOutput.write(ct);
                    //writeTime++;
                }
                nowPos = (deep+nowPos)- (k<<3) ;
                buffer = (byte)((code<<(8-nowPos ))&0xff);
            }else{
                /*添加后编码不够一个字节长度 */
               if(nowPos+deep =={
                    ct = (byte)code;
                    ct = (byte)((ct)|(buffer));
                    hfmOutput.write(ct);
                    writeTime++;
                    nowPos = 0;
                    buffer =0;
         }else{
                    nowPos += deep;
                    ct = (byte)((code<<(8 - nowPos))& 0xff);
                    buffer =(byte)(ct|buffer);
         }
            }
       
     }
            /*如果还有剩余数据写入*/ 
            if(nowPos >0){
                  hfmOutput.write(buffer);
          }
            //System.out.println("\nOK!"+hfmOutput.size()+"  bit"+bit + " i:"+i+" writeTimt:"+writeTime);
        return hfmOutput.toByteArray();
}

/*************************   最后介绍一下解码过程吧   *************************/
     public byte[] getunCodeBytes()throws Exception{
        if(this.buf[0] != 'B'||this.buf[1]!=-1 ||this.count<12||buf[8] != -1 || buf[9] != -1 ){
            throw new UnsupportedEncodingException();
        }
        int nowPos = 10;
        int contentLength;
        int dataLength = buf[6]&0x000000ff;
        int weightSize = buf[7];
        if(dataLength == 0& weightSize!= 0){
            dataLength = 256;
        }
        int i,j,k;
        int intTmp;
        byte byteTmp;
        byte[] bytesTmp = new byte[4];
        bytesTmp[0] = buf[2];
        bytesTmp[1] = buf[3];
        bytesTmp[2] = buf[4];
        bytesTmp[3] = buf[5];
        contentLength = bytesToInt(bytesTmp);
       
        for(i=0;i<4;i++){
            bytesTmp[i] = 0;
        }
        /*读取权表*/
        for(i=0;i<dataLength;i++){
            byteTmp = buf[nowPos++];
            for(j =0 ;j<weightSize;j++){
                bytesTmp[j] = buf[nowPos++];
            }
            intTmp = bytesToInt(bytesTmp);
            bhm.append(byteTmp, intTmp);
        }
        if(buf[nowPos++] != -1|| buf[nowPos++]!= -1){
            throw new UnsupportedEncodingException();
        }
        /* 建树编码*/
        bhm.createTree();
        //bhm.codeTree();
        boolean rchild;
        int time = 0;
        ByteHfmNode tmp;
        /*权表读取完毕,开始解码*/
  while(nowPos<count){
  byteTmp = buf[nowPos++];
  for(j = 7;j>=0;j--){
   intTmp = (byteTmp>>j);
                        intTmp = intTmp&0x1;
   rchild = (intTmp == 1 );
   if(bhm.searchNode(rchild)){
    time++;
                                tmp =bhm.getSearchNode();
    bos.write(tmp.data);
    if(time >= contentLength){
     //System.out.println(time+" "+contentLength);
     break;
    }
   }
  }
                byteTmp = 0;
}
        return bos.toByteArray();
}


使用大文件压缩器时间 
(压缩一个 2.09m 的exe文件 时间: 273 ms 压缩后 1.71m ,
一个 61.3m 的exe 时间:2723ms 压缩后文件大小基本没变 )
压缩率跟哈弗曼算法有关

添加一个C#移植版,需要.net framework

 

  • 大小: 71.7 KB
2
3
分享到:
评论
3 楼 s381729867 2015-04-21  
引用
[i][/i]
楼主  能不能发一下源码 啊  很急 啊  381729867@qq.com
2 楼 s381729867 2015-04-21  
能不能发一下 源码啊     楼主   381729867@qq.com
1 楼 qiaoweishu 2010-11-19  
主人很用心,学习了。

相关推荐

    基于C++的哈弗曼编/译码器

    哈弗曼编码是一种高效...总之,这个基于C++的哈弗曼编/译码器项目涉及到了数据压缩的基础理论,二叉树的构建,编码和解码算法的实现,以及C++的文件处理等多个方面的知识,对于理解和应用哈弗曼编码有很好的实践价值。

    哈弗曼编/译码器(C语言)

    哈弗曼编码是一种高效的数据压缩方法,它基于一种特殊的二叉树——哈弗曼树(Huffman Tree),也称为最优二叉树。这种编码技术在信息传输和存储中发挥着重要作用,特别是在图像处理(如JPEG)、文本压缩等领域广泛...

    试验四 哈弗曼编/译码器

    哈弗曼编码是一种高效的数据压缩方法,通过构建哈弗曼树来实现。在给定的题目中,我们看到两个不同的构建哈弗曼树的方法:方案1是直接输入字符及其权值,而方案2是根据文档中字符的频率来构建。 在哈弗曼树的结构中...

    C++语言实现的哈夫曼编码/译码器

    根据给定文件的信息,我们可以提炼出关于“C++语言实现的哈夫曼编码/译码器”的关键知识点,包括但不限于: ### 哈夫曼编码原理 哈夫曼编码是一种可变长度的前缀编码技术,它根据字符出现频率来构建一棵哈夫曼树,...

    哈弗曼编码译码器 Huffman译码器

    ### 哈弗曼编码译码器:Huffman译码器 #### 一、概述 哈弗曼编码(Huffman Coding)是一种广泛应用于数据压缩领域的编码方式,它是由David A. Huffman在1952年提出的。哈弗曼编码的核心思想是通过构建一棵特殊的...

    哈弗曼树编码译码系统

    这个压缩文件包含了实现哈弗曼编码译码的完整代码,适用于数据结构课程设计。学生可以从中学习到如何运用链表、树等数据结构以及算法来实现这一过程。同时,此项目还可以帮助学生理解数据压缩的基本原理和实际应用,...

    哈弗曼编码译码器的实现

    5. **哈弗曼译码器**:译码器主要包括: - 读取比特流:`Read()`函数用于从压缩文件中读取比特。 - 重建哈弗曼树:`CreateFromCodeFile()`或`CreateFromSourceFile()`函数根据编码文件或原始文件头重建哈弗曼树。 ...

    数据结构课程设计报告_哈弗曼编码.doc

    通过对哈弗曼编码/译码器的设计和实现,学生能够全面掌握数据结构和算法在实际问题中的应用,同时提升编程实践能力。这个过程不仅是对专业知识的巩固,也是对个人解决问题能力的锻炼。通过课程设计,学生将能够更好...

    C/C++哈弗曼编码,译码

    请设计一个哈夫曼编码器/译码器。 【基本要求】 (1)初始化:键盘输入n个字符和n个权值,建立哈夫曼树; (2)编码:利用已建立的huffman树生成huffman编码 ; (3)译码:利用已建立的哈夫曼树对一段代码进行译码...

    哈弗曼编码译码器

    总的来说,哈弗曼编码译码器是实现数据压缩的重要工具,它的C++实现涉及数据结构、算法和文件操作等多个方面的知识。理解和掌握哈弗曼编码不仅可以提高数据压缩的效率,也是深入理解计算机科学和信息处理的关键一步...

    哈夫曼编码/译码器 C++数据结构课程设计

    4. **译码功能**:译码是哈夫曼编码的逆过程。给定一个编码,需要按照哈夫曼树的结构,从根节点开始,根据0和1的序列决定向左还是向右移动,直至到达叶节点,这样就可以确定原始字符。 5. **数据结构设计**:在C++...

    使用哈弗曼树编码译码器

    在C++中实现哈弗曼编码译码器,我们需要理解以下几个关键步骤: 1. **构建哈弗曼树**: - 首先,统计输入字符的频率,创建一个频率列表。 - 使用优先队列(通常用最小堆实现)存储哈弗曼树节点,其中包含字符及其...

    数据结构课程设计 哈弗曼编码译码器源代码 java版

    这个Java版的哈弗曼编码译码器是一个很好的学习资源,适合加深对数据结构和算法的理解,特别是对于那些希望掌握数据压缩技术的学生或开发者。通过阅读和分析源代码,你可以了解到如何将理论知识应用于实际编程中,...

    哈夫曼译码流程图

    哈夫曼译码流程图,数据结构课程设计需要,用visio画的

    哈弗曼编码译码器分享.pdf

    《哈弗曼编码译码器实现详解》 哈弗曼编码是一种有效的数据压缩方法,它通过构建哈弗曼树来生成具有最短路径长度的编码,使得频度高的字符拥有较短的编码,从而提高编码效率。在哈弗曼编码器中,主要涉及以下几个...

    哈夫曼编/译码器

    哈夫曼编/译码器 问题描述 利用哈夫曼编码进行信息通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来的数据进行译码(复原)...

    哈弗曼编码译码器C++代码

    ### 哈弗曼编码译码器C++代码解析与知识点总结 #### 一、哈弗曼编码原理 哈弗曼编码(Huffman Coding)是一种广泛应用于数据压缩领域的编码技术,它采用一种自底向上的贪心算法来构建最优前缀码。这种编码方式能够...

Global site tag (gtag.js) - Google Analytics