LZW算法原理及实现

LZW算法原理及实现

原理

LZW的想在数据中创建一个短语词典。如果在此后的编码过程中又遇到了相同的字典,则用相应的索引号替代,而不是短语本身。

由于LZW字典产生的规则固定,所以不需要额外传递字典;解码端可以采取逆过程重建出来字典并进行解码。

编码

编码的流程大致如下:

步骤1:将词典初始化为包含所有可能的单字符,当前前缀P初始化为空。

步骤2:当前字符C=字符流中的下一个字符。

步骤3:判断P+C是否在词典中

  • 如果“是”,则用C扩展P,即让P=P+C,返回到步骤2。

  • 如果“否”,则

    输出与当前前缀P相对应的码字W;

    将P+C添加到词典中;

    令P=C,并返回到步骤2

解码

解码的流程大致如下:

步骤1:在开始译码时词典包含所有可能的前缀根。

步骤2:令CW:=码字流中的第一个码字。

步骤3:输出当前缀-符串CW到码字流。

步骤4:先前码字PW=当前码字CW。

步骤5:当前码字CW=码字流的下一个码字。

步骤6:判断当前缀-符串CW 是否在词典中。

  • 如果”是”,则把当前缀-符串CW输出到字符流。

    • 当前前缀P=PW。
    • 当前字符C=当前前缀字符串的第一个字符。
    • 把P+C添加到词典。
  • 如果”否”,则当前前缀P=PW。

    • 当前字符C=当前字符串CW的第一个字符。
    • 输出P+C到字符流,然后把它添加到词典中。

实现

位输入输出工具

因为索引号大于等于255,所以有时候1字节不够的,而是用int型数据又会造成大量字节的浪费,所以要根据索引的最大值合理选择存储所需要的位数,这就要用到按位进行输入输出的工具了。

struct bitWriter
{
   
   
    char buffer;
    int pos, bits;
    ofstream &out;
    bitWriter( ofstream &out ): out(out) {
   
    buffer = pos = bits = 0;}
    void WriteBit( int bit )
    {
   
   
        if( pos == 8 )
        {
   
   
            out.write( &buffer, 1 );
            pos = 0; buffer = 0;
        }
        buffer |= ( bit << pos);
        pos ++; bits ++;
    }
    void Write( int value, int len )
    {
   
   
        while( len -- )
        {
   
   
            WriteBit( value & 1 );
            value >>= 1;
        }
    }
    void EndWrite()
    {
   
   
        out.write(&buffer,1);
    }
};
struct bitReader
{
   
   
    char buffer;
    int pos, len, bits;
    ifstream &in;
    bitReader( ifstream &in ): in(in)
    {
   
   
        buffer = 0; pos = 8;
        in.seekg(0,ios::end);
        len = in.tellg(); bits = len * 8;
        in.seekg(0,ios::beg);
    }
    unsigned int ReadBit()
    {
   
   
        if( pos == 8 )
        {
   
   
            buffer = in.get();
            pos = 0;
        }
        int t = buffer & 1;
        buffer >>= 1; pos ++;
        return t;
    }
    unsigned int ReadBits( int n )
    {
   
   
        unsigned int ans = 0;
        for( int i = 0; i < n; ++ i )
        {
   
   
            ans |= ( ReadBit() << i );
        }
        return ans;
    }
};

编码器

利用Trie树来存储字典。分析发现编码所用的节点只需要存储字符的编码和子节点地址即可。

为了减少存储空间的同时保持一定的速度,使用unorder_map来存储节点地址。

最终时间复杂度为 O ( n ) O(n) O(n), n n n为文件长度。

struct encoderNode
{
   
   
    int code;
    unordered_map< char, encoderNode* > index;
    encoderNode(){
   
    code = -1; index.clear(); }
    ~encoderNode()
    {
   
   
        for( auto i : index )
            if( nullptr != i.second )
                delete i.second;
        index.clear();
    }
};
class LZWEncoder
{
   
   
private:
    encoderNode* root;
    encoderNode* pointer;
    int tot, maxValue;
    vector< int > coded_file;
public:
    LZWEncoder() {
   
    root = new encoderNode; pointer = root; tot = maxValue = 255;}
    ~LZWEncoder() {
   
    delete root; }
    void Init()
    {
   
   
        /*构建最初始的字典。*/
        root = new encoderNode; tot = 256;
        pointer = root;
        for( int i = 0; i < 256; ++ i )
        {
   
   
            auto t = root->index[i] = new encoderNode;
            t->code = i;
        }
    }
    vector< int > Encode( unsigned char* buffer, int len )
    {
   
   
        int pos = 0;
        char c;
        while( pos < len )
        {
   
   
            //读取下一个字符
            c = buffer[pos ++];
            //如果当前字符串p+下一个字符不在字典中
            if( nullptr == pointer->index[c] )
            {
   
   
                //将当前字符串p的编号输出
                coded_file.push_back( pointer->code );
                //将p+c加入字典
                pointer = ( pointer->index[c] = new encoderNode );
                pointer->code = tot ++;
                pointer = root->index[c];
            }
            else pointer = pointer->index[c]; //如果在字典中就继续读取下一个字符,直到不再其中
        }
        if( len == pos ) coded_file.push_back( pointer->code ); //将未输出的部分输出
        return coded_file;
    }
    vector< int > Encode( ifstream &in )
    {
   
   
        //重载,直接从文件流中进行解码
        int pos = 0; char c;
        in.seekg(0, ios::end); int len = in.tellg(); in.seekg(0, ios::beg);
        unsigned char *buffer = new unsigned char[len];
        in.read((char*) buffer, len );
        auto v = Encode( buffer, len );
        delete [] buffer;
        return v;
    }
    vector< int > Encode( bitReader &reader )
    {
   
   
        //重载,从位工具中读取数据并解码
        char c; int len = reader.len;
        for( int i = 0; i < len; ++ i )
        {
   
   
            //读取下一个字符
            c = reader.ReadBits(8);
            //如果当前字符串p+下一个字符不在字典中
            if( nullptr == pointer->index[c] )
            {
   
   
                //将当前字符串p的编号输出
                coded_file.push_back( pointer->code );
                //将p+c加入字典
                pointer = ( pointer->index[c] = new encoderNode );
                pointer->code = tot ++;
                pointer = root->index[c];
            }
            else pointer = pointer->index[c]; //如果在字典中就继续读取下一个字符,直到不再其中
        }
        coded_file
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值