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;
}