1. 程式人生 > >LZW 壓縮演算法的C++實現

LZW 壓縮演算法的C++實現

最近老師佈置了一個上機作業,實現LZW 。剛好最近對STL掌握的還不錯,寫起來就比較容易,又一次體會到了C++模版庫的強大,要不然程式碼量就*2了。

不知道老師的要求是對ASCCI碼所有的字元進行壓縮編碼還是隻對輸入字串的進行編碼,不過演算法都一樣,我是對根據輸入的串進行構造字典表,如果直接對256個字元進行構造,那更簡單了。

結下來就先說一說LZW的演算法,它屬於無失真壓縮的一種,尤其對輸入重複的串壓縮的越徹底。

基礎思想:

利用字元的重用性,每當輸出一個編碼,就講一個新的string 存放到字典表中

演算法流程:

1.初始化,將所有的單字元放入字典表中(這裡的字元是指輸入字串中不重複的字元)

2.讀入一個輸入給字首串,賦值給W

3.讀入一個輸入字元,賦值給K

判斷:

 if 沒有這樣的K(指輸入的字串結束了),就輸出最後一個W代表的碼字,break;

if WK(指將WK兩個字串合併成一個)存在於字典表

{W=WK;repeat3;}

if WK不再字典表

{輸出W代表的碼字,WK加進字典表;W=K;repeat3;}

具體可以看看下面的示例:

輸入串ABABB

構造初始字典表並同時進行編號(即流程中所說的碼字):

A 1
B 2

接下來第一個流程:

W=A

K=B 

判斷WK=AB不在字典表中,輸出W代表的碼字A--1,WK=AB加入字典表,W=K=B,repeat3

A 1
B 2
AB 3

第二個流程:

W=B

K=A 

判斷WK=BA不在字典表中,輸出W代表的碼字B--2,WK=BA加入字典表,W=K=A,repeat3

A 1
B 2
AB 3
BA 4

第三個流程:

W=A

K=B

判斷WK=AB在字典表中,W=WK=AB,repeat3

第四個流程:

W=AB

K=B

判斷WK=ABB不再字典表中,輸出W代表的碼字AB--3,WK=ABB加入字典表,W=K=B,repeat3

A 1
B 2
AB 3
BA 4
ABB 5
第五個流程:

W=B

K='\0' 輸入窮盡,輸出W代表的碼字B--2

綜上最後的壓縮後的編碼為 1232. 

我這個可能是按照流程走的一遍比較麻煩,不過如果大家有耐心的話走一遍就全部明白了

解碼就是壓縮的逆過程,根據輸入的編碼流從字典表中找字元,簡單。

實現程式碼如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#include <vector>
using namespace std;
long len=0;//原字串的長度
long loc=0;//去重之後字串的長度
map<string,long> dictionary;
vector <long> result;
#define MAX 100;
void LZWcode(string a,string s)
{
    //memset(&result,0,sizeof(int));
    string W,K;
    for(long i=0;i<loc;i++)
    {
        string s1;
        s1=s[i];//將單個字元轉換為字串
        dictionary[s1]=i+1;
    }
    W=a[0];
    loc+=1;
    for(int i=0;i<len-1;i++)
    {
        K=a[i+1];
        string firstT=W;
        string secontT=W;
        if(dictionary.count(firstT.append(K))!=0)//map的函式count(n),返回的是map容器中出現n的次數
            W=firstT;
        else
        {
            result.push_back(dictionary[W]);
            dictionary[secontT.append(K)]=loc++;
            W=K;
        }
    }
    if(!W.empty())
        result.push_back(dictionary[W]);
    for(int i=0;i<result.size();i++)
        cout<<result[i];
}

void LZWdecode(int *s,int n)
{
    string nS;
    for(int i=0;i<n;i++)
        for(map<string,long>::iterator it=dictionary.begin(); it!=dictionary.end();it++)
            if(it->second==s[i])
            {
                cout<<it->first<<" ";
            }
    for(map<string,long>::iterator it=dictionary.begin(); it!=dictionary.end();it++)//輸出壓縮編碼的字典表
        cout<<it->first<<" "<<it->second<<endl;
}
int main(int argc, char const *argv[])
{
    cout<<"本程式的解碼是根據輸入的編碼字元進行的解碼,並不是全256 的字元"<<endl;
    cout<<"選擇序號:"<<endl;
    cout<<"1.壓縮編碼   2.解碼"<<endl;
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        switch(n)
        {
            case 1:
            {
                char s[100],a[100];
                cout<<"輸入一串字元:"<<endl;
                cin>>s;
                len=strlen(s);
                for(int i=0;i<len;i++)
                    a[i]=s[i];
                sort(s,s+len);//排序
                loc=unique(s,s+len)-s;//去重
                LZWcode(a,s);
                break;
            }
            case 2:
            {
                cout<<"輸入解碼陣列的長度:"<<endl;
                int changdu;
                cin>>changdu;
                cout<<"輸入解碼數串(每個數串以空格隔開):"<<endl;
                int s[changdu];
                for(int i=0;i<changdu;i++)
                    cin>>s[i];
                LZWdecode(s, changdu);
                break;
            }
            default:
                cout<<"你的輸入不正確,請從重新開始"<<endl;
        }
        if(n==2)
        {
            auto iter=result.begin();   // 每次正確輸入結束後對結果進行清零
            while(iter!=result.end())
                result.erase(iter++);
        }
    }
    return 0;
}