1. 哈夫曼實現壓縮
我的作業,哈弗曼樹的建立,不過那個字元的頻率要自己編的。
//main.cpp
#include"HuffmanTree.h"
#include<string.h>
#include<stdlib.h>
//#include<iostream>
//using namespace std;
int main(){
HuffmanTree huftree;
char Choose;
while(1){
cout<<"\n\n**********************歡迎使用哈夫曼編碼/解碼系統**********************\n"<<endl<<endl;
cout<<" 您可以進行以下操作:\n";
cout<<" 1 建立哈夫曼樹\n";
cout<<" 2 編碼(源文已在文件ToBeTran中,或鍵盤輸入)\n";
cout<<" 3 解碼(碼文已在文件CodeFile中)\n";
cout<<" 4 顯示碼文\n";
cout<<" 5 顯示哈夫曼樹\n";
cout<<" 6 退出\n\n";
cout<<" 請選擇一個操作:";
cin>>Choose;
switch(Choose)
{
case '1':
huftree.CreateHuffmanTree();
break;
case '2':
huftree.Encoder();
break;
case '3':
huftree.Decoder();
break;
case '4':
huftree.PrintCodeFile();
break;
case '5':
huftree.PrintHuffmanTree();
break;
case '6':
cout<<"\n**********************感謝使用本系統!*******************\n\n";
system("pause");
return 0;
}//switch
}//while
}//main
//Huffmannode.h
#ifndef _HuffmanNode_
#define _HuffmanNode_
struct HuffmanNode
{
int weight; //存放結點的權值,假設只考慮處理權值為整數的情況
int parent; //-1表示為根結點,否則表示為非根結點
int lchild,rchild; //分別存放該結點的左右孩子的所在單元的編號
};
#endif
//huffmantree.h
#ifndef _HuffmanTree_
#define _HuffmanTree_
#include"HuffmanNode.h"
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
class HuffmanTree //哈夫曼樹
{
public:
struct HuffmanNode *Node; //Node[]存放哈夫曼樹
char *Info; //Info[]存放源文用到的字元——源碼,如'a','b','c','d','e',此內容可以放入結點中,不單獨設數組存放
int LeafNum; //哈夫曼樹的葉子個數,也是源碼個數
public:
HuffmanTree();
~HuffmanTree();
void CreateHuffmanTree(); /*在內存中建立哈夫曼樹,存放在Node[]中。 讓用戶從兩種建立哈夫曼樹的方法中選擇:
1.從鍵盤讀入源碼字元集個數,每個字元,和每個字元的權重,建立哈夫曼樹,
並將哈夫曼樹寫入文件hfmTree中。2.從文件hfmTree中讀入哈夫曼樹信息,建立哈夫曼樹*/
void CreateHuffmanTreeFromKeyboard();
void CreateHuffmanTreeFromFile();
void Encoder(); /*使用建立好的哈夫曼樹(如果不在內存,則從文件hfmTree中讀入並建立內存里的哈夫曼樹),
對文件ToBeTran中的正文進行編碼,並將碼文寫入文件CodeFile中。
ToBeTran的內容可以用記事本等程序編輯產生。*/
void Decoder(); /*待解碼的碼文存放在文件CodeFile中,使用建立好的哈夫曼樹(如果不在內存,
則從文件hfmTree中讀入並建立內存里的哈夫曼樹)將碼文解碼,
得到的源文寫入文件TextFile中,並同時輸出到屏幕上。*/
void PrintCodeFile(); /*將碼文文件CodeFile顯示在屏幕上*/
void PrintHuffmanTree(); /*將哈夫曼樹以直觀的形式(凹入表示法,或廣義表,或其他樹形表示法)顯示在屏幕上,
同時寫入文件TreePrintFile中*/
void PrintHuffmanTree_aoru(int T,int layer=1); /*凹入表示法顯示哈夫曼樹,由PrintHuffmanTree()調用*/
};
#endif
//huffmantree.cpp
#include"HuffmanTree.h"
#include<string>
#include<limits> //為使用整型最大值
//#include<iostream>
using namespace std;
//******************************************************
HuffmanTree::HuffmanTree()
{
LeafNum=0;
Node=NULL;
Info+NULL;
}
//******************************************************
HuffmanTree::~HuffmanTree()
{
delete []Node;
Node=NULL;
delete []Info;
Info=NULL;
}
//******************************************************
void HuffmanTree::CreateHuffmanTree()
{
char Choose;
cout<<"你要從文件中讀入哈夫曼樹(按1),還是從鍵盤輸入哈夫曼樹(按2)?";
cin>>Choose;
if(Choose=='2') {//鍵盤輸入建立哈夫曼樹
CreateHuffmanTreeFromKeyboard();
}//choose=='2'
else { //從哈夫曼樹文件hfmTree.dat中讀入信息並建立哈夫曼樹
CreateHuffmanTreeFromFile();
}
}
//******************************************************
void HuffmanTree::CreateHuffmanTreeFromKeyboard()
{
int Num;
int i,j,pos1,pos2,max1,max2;
cout<<"\n請輸入源碼字元集個數:";
cin>>Num;
if (Num<=1)
{
cout<<"無法建立少於2個葉子結點的哈夫曼樹。\n\n";
return;
}
LeafNum=Num;
Node=new HuffmanNode[2*Num-1];
Info=new char[2*Num-1];
for( i=0;i<Num;i++) {//讀入哈夫曼樹的葉子結點信息
cout<<"請輸入第"<<i+1<<"個字元值";
getchar();
Info[i]=getchar(); //源文的字元存入字元數組Info[]
getchar();
cout<<"請輸入該字元的權值或頻度";
cin>>Node[i].weight; //源文的字元權重存入Node[].weight
Node[i].parent=-1; //為根結點
Node[i].lchild=-1; //無左孩子
Node[i].rchild=-1; //無右孩子
}
for( i=Num;i<2*Num-1;i++) //循環建立哈夫曼樹內部結點 表示需做Num-1次合並
{
pos1=-1;pos2=-1; //分別用來存放當前最小值和次小值的所在單元編號
max1=32767; max2=32767; //32767為整形數的最大值 分別用來存放當前找到的最小值和次小值
for(j=0;j<i;j++) //在根結點中選出權值最小的兩個
{
if(Node[j].parent==-1) //是否為根結點
if(Node[j].weight<max1) //是否比最小值要小
{
max2=max1; //原最小值變為次小值
max1=Node[j].weight; //存放最小值
pos2=pos1; //修改次小值所在的單元編號
pos1=j; //修改最小值所在的單元編號
}
else
if(Node[j].weight<max2) //比原最小值大但比原此小值小
{
max2=Node[j].weight;
pos2=j;
}
}
Node[pos1].parent=i;
Node[pos2].parent=i;
Node[i].lchild=pos1;
Node[i].rchild=pos2;
Node[i].parent=-1;
Node[i].weight=Node[pos1].weight+Node[pos2].weight;
} //for
LeafNum=Num;
cout<<"哈夫曼樹已成功構造完成。\n";
//把建立好的哈夫曼樹寫入文件hfmTree.dat
char ch;
cout<<"是否要替換原來的哈夫曼樹文件(Y/N):";
cin>>ch;
if (ch!='y'&&ch!='Y') return;
ofstream fop;
fop.open("hfmTree.dat",ios::out|ios::binary|ios::trunc); //打開文件
if(fop.fail()) {
cout<<"\n哈夫曼樹文件打開失敗,無法將哈夫曼樹寫入hfmTree.dat文件。\n";
return;
}
fop.write((char*)&Num,sizeof(Num)); //先寫入哈夫曼樹的葉子結點個數
for( i=0;i<Num;i++) { //再寫入源文字元集的所有字元(存儲在Info[]中)
fop.write((char*)&Info[i],sizeof(Info[i]));
flush(cout);
}
for( i=0;i<2*Num-1;i++) { //最後寫入哈夫曼樹的各個結點(存儲在Node[]中)
fop.write((char*)&Node[i],sizeof(Node[i]));
flush(cout);
}
fop.close(); //關閉文件
cout<<"\n哈夫曼樹已成功寫入hfmTree.dat文件。\n";
}
//******************************************************
void HuffmanTree::CreateHuffmanTreeFromFile()
{
ifstream fip;
fip.open("hfmTree.dat",ios::binary|ios::in);
if(fip.fail()) {
cout<<"哈夫曼樹文件hfmTree.dat打開失敗,無法建立哈夫曼樹。\n";
return;
}
fip.read((char*)&LeafNum,sizeof(LeafNum));
if (LeafNum<=1) {
cout<<"哈夫曼樹文件中的數據有誤,葉子結點個數少於2個,無法建立哈夫曼樹。\n";
fip.close();
return;
}
Info=new char[LeafNum];
Node=new HuffmanNode[2*LeafNum-1];
for( int i=0;i<LeafNum;i++)
fip.read((char*)&Info[i],sizeof(Info[i]));
for(int i=0;i<2*LeafNum-1;i++)
fip.read((char*)&Node[i],sizeof(Node[i]));
fip.close();
cout<<"哈夫曼樹已成功構造完成。\n";
}
//******************************************************
void HuffmanTree::Encoder()
{
if(Node==NULL) //內存沒有哈夫曼樹,則從哈夫曼樹文件hfmTree.dat中讀入信息並建立哈夫曼樹
{
CreateHuffmanTreeFromFile();
if (LeafNum<=1)
{
cout<<"內存無哈夫曼樹。操作撤銷。\n\n";
return;
}
}//if
char *SourceText; //字元串數組,用於存放源文
//讓用戶選擇源文是從鍵盤輸入,還是從源文文件ToBeTran.txt中讀入
char Choose;
cout<<"你要從文件中讀入源文(按1),還是從鍵盤輸入源文(按2)?";
cin>>Choose;
if(Choose=='1')
{
ifstream fip1("ToBeTran.txt");
if(fip1.fail())
{
cout<<"源文文件打開失敗!無法繼續執行。\n";
return;
}
char ch;
int k=0;
while(fip1.get(ch)) k++; //第一次讀文件只統計文件中有多少個字元,將字元數存入k
fip1.close();
SourceText=new char[k+1]; //申請存放源文的字元數組空間
ifstream fip2("ToBeTran.txt");//第二次讀源文文件,把內容寫入SourceText[]
k=0;
while(fip2.get(ch)) SourceText[k++]=ch;
fip2.close();
SourceText[k]='\0';
cout<<"需編碼的源文為:";
cout<<SourceText<<endl;
}
else { //從鍵盤輸入源文
string SourceBuff;
cin.ignore();
cout<<"請輸入需要編碼的源文(可輸入任意長,按回車鍵結束):\n";
getline(cin,SourceBuff,'\n');
int k=0;
while(SourceBuff[k]!='\0')
k++;
SourceText=new char[k+1];
k=0;
while(SourceBuff[k]!='\0') {
SourceText[k]=SourceBuff[k];
k++;
}
SourceText[k]='\0';
}
ofstream fop("CodeFile.dat",ios::trunc); //打開碼文存放文件
char *code;
code=new char[LeafNum]; //存放一個源文字元的編碼
int k=0;
int i,j,start;
while(SourceText[k]!='\0') //源文串中從第一個字元開始逐個編碼
{
start=0;
for(i=0;i<LeafNum;i++)
if(Info[i]==SourceText[k]) //求出該文字所在單元的編號
break;
j=i;
while(Node[j].parent!=-1) //結點j非根
{
j=Node[j].parent; //求結點j的雙親結點
if(Node[j].lchild==i) //是左子樹,則生成代碼0
code[start++]='0';
else
code[start++]='1'; //是右子樹,則生成代碼1
i=j;
}
code[start]='\0'; //置串結束符
for(i=0;i<start/2;i++)
{
j=code[i];
code[i]=code[start-1-i];
code[start-1-i]=j;
}
i=0; //將源文的當前字元的對應編碼寫入碼文文件
while(code[i]!='\0')
{
fop<<code[i];
i++;
}
k++; //源文串中的字元後移一個
}
fop.close();
cout<<"已完成編碼,碼文已寫入文件CodeFile.dat中。\n\n";
}
//******************************************************
void HuffmanTree::Decoder()
{
//如果內存沒有哈夫曼樹,則從哈夫曼樹文件hfmTree.dat中讀入信息並建立哈夫曼樹
if(Node==NULL)
{
CreateHuffmanTreeFromFile();
if (LeafNum<=1)
{
cout<<"內存無哈夫曼樹。操作撤銷。\n\n";
return;
}
}
//將碼文從文件CodeFile.dat中讀入 CodeStr[]
ifstream fip1("CodeFile.dat");
if(fip1.fail())
{
cout<<"沒有碼文,無法解碼。\n";
return;
}
char* CodeStr;
int k=0;
char ch;
while(fip1.get(ch))
{
k++;
}
fip1.close();
CodeStr=new char[k+1];
ifstream fip2("CodeFile.dat");
k=0;
while(fip2.get(ch))
CodeStr[k++]=ch;
fip2.close();
CodeStr[k]='\0';
cout<<"經解碼得到的源文為:";
ofstream fop("TextFile.dat");
int j=(LeafNum-1)*2; //j指向哈夫曼樹的根
int i=0; //碼文從第一個符號開始,順著哈夫曼樹由根下行,按碼文的當前符號決定下行到左孩子還是右孩子
while(CodeStr[i]!='\0') //下行到哈夫曼樹的葉子結點處,則譯出葉子結點對應的源文字元
{
if(CodeStr[i]=='0')
j=Node[j].lchild; //往左走
else
j=Node[j].rchild; //往右走
if(Node[j].rchild==-1) //到達葉子結點
{
cout<<Info[j]; //輸出葉子結點對應的字元
j=LeafNum*2-1-1; //表示重新從根結點開始往下搜索
}
i++;
}
fop.close();
cout<<"\n解碼成功且已存到文件TextFile.dat中。\n\n";
}
//******************************************************
void HuffmanTree::PrintCodeFile()
{
char ch;
int i=1;
ifstream fip("CodeFile.dat");
ofstream fop("CodePrin.dat");
if(fip.fail())
{
cout<<"沒有碼文文件,無法顯示碼文文件內容。\n";
return;
}
while(fip.get(ch))
{
cout<<ch;
fop<<ch;
if(i==50)
{
cout<<endl;
fop<<endl;
i=0;
}
i++;
}
cout<<endl;
fop<<endl;
fip.close();
fop.close();
}
//******************************************************
void HuffmanTree::PrintHuffmanTree()
{
//如果內存沒有哈夫曼樹,則從哈夫曼樹文件hfmTree.dat中讀入信息並建立哈夫曼樹
if(Node==NULL)
{
CreateHuffmanTreeFromFile();
if (LeafNum<=1) {
cout<<"內存無哈夫曼樹。操作撤銷。\n\n";
return;
}
}
ofstream fop("TreePrint.dat",ios_base::trunc);
fop.close();
PrintHuffmanTree_aoru(2*LeafNum-1-1);
return;
}
//******************************************************
void HuffmanTree::PrintHuffmanTree_aoru(int T,int layer)//凹入表示法
{
if(Node[T].lchild!=-1)
PrintHuffmanTree_aoru(Node[T].lchild,layer+1);//左子樹
for(int i=1;i<=layer;i++)// 根
cout<<'\0'<<'\0'<<'\0';
cout<<Node[T].weight<<endl;
if(Node[T].rchild!=-1)
PrintHuffmanTree_aoru(Node[T].rchild,layer+1); // 右子樹
}
2. 哈夫曼壓縮演算法的內容是什麼
注:哈夫曼和lzss演算法不是同一種演算法,先用哈夫曼再用lzss演算法壓縮後會發現經哈夫曼壓縮後再用lzss壓縮文件會變大,具體原因不明
lzss原理:
把編碼位置置於輸入數據流的開始位置。
在前向緩沖器中查找窗口中最長的匹配串
①
pointer
:=匹配串指針。
②
length
:=匹配串長度。
判斷匹配串長度length是否大於等於最小匹配串長度(min_length)
,
如果「是」:輸出指針,然後把編碼位置向前移動length個字元。
如果「否」:輸出前向緩沖存儲器中的第1個字元,然後把編碼位置向前移動一個字元。
如果前向緩沖器不是空的,就返回到步驟2。
例:編碼字元串如表03-05-3所示,編碼過程如表03-05-4所示。現說明如下:
「步驟」欄表示編碼步驟。
「位置」欄表示編碼位置,輸入數據流中的第1個字元為編碼位置1。
「匹配」欄表示窗口中找到的最長的匹配串。
「字元」欄表示匹配之後在前向緩沖存儲器中的第1個字元。
「輸出」欄的輸出為:
①
如果匹配串本身的長度length
>=
min_length,輸出指向匹配串的指針,格式為(back_chars,
chars_length)。該指針告訴解碼器「在這個窗口中向後退back_chars個字元然後拷貝chars_length個字元到輸出」。
②
如果匹配串本身的長度length
>=
min_length,則輸出真實的匹配串。
表:輸入數據流
位置
1234567891011
字元
aabbcbbaabc
表:編碼過程(min_length
=
2)
步驟位置匹配串輸出
11--a
22aa
33--
b
44bb
55--c
66b
b(3,2)
78
a
a
b(7,3)
811cc
3. 哈夫曼樹怎麼壓縮和解壓 c++
http://blog.csdn.net/leex_brave/article/details/51598359
4. 哈夫曼編碼壓縮概念的基本思想如何回答(精簡的說)
哈夫曼編碼(Huffman Coding)是一種編碼方式,哈夫曼編碼是可變字長編碼(VLC)的一種。 Huffman於1952年提出一種編碼方法,該方法完全依據字元出現概率來構造異字頭的平均長 度最短的碼字,有時稱之為最佳編碼,一般就叫作Huffman編碼。 以哈夫曼樹─即最優二叉樹,帶權路徑長度最小的二叉樹,經常應用於數據壓縮。 在計算機信息處理中,「哈夫曼編碼」是一種一致性編碼法(又稱"熵編碼法"),用於數據的無損耗壓縮。這一術語是指使用一張特殊的編碼表將源字元(例如某文件中的一個符號)進行編碼。這張編碼表的特殊之處在於,它是根據每一個源字元出現的估算概率而建立起來的(出現概率高的字元使用較短的編碼,反之出現概率低的則使用較長的編碼,這便使編碼之後的字元串的平均期望長度降低,從而達到無損壓縮數據的目的)。這種方法是由David.A.Huffman發展起來的。 例如,在英文中,e的出現概率很高,而z的出現概率則最低。當利用哈夫曼編碼對一篇英文進行壓縮時,e極有可能用一個位(bit)來表示,而z則可能花去25個位(不是26)。用普通的表示方法時,每個英文字母均佔用一個位元組(byte),即8個位。二者相比,e使用了一般編碼的1/8的長度,z則使用了3倍多。倘若我們能實現對於英文中各個字母出現概率的較准確的估算,就可以大幅度提高無損壓縮的比例。
5. 哈夫曼編碼法的壓縮和解壓縮怎麼實現
建立一棵赫夫曼樹,設每個父節點的左子節點為1,右子節點為0,然後由根節點到所要編碼的字元的葉節點的路徑確定字元的編碼。比如要編碼a,假設a在第三層,則由根節點到a的路徑為:根節點——右子節點(0)——左子節點(1)。那麼a的編碼就為01。就這樣把所有字元進行編碼,建立一個赫夫曼編碼表。利用這個編碼表把字元串編碼就是壓縮了,解壓縮就是把參照赫夫曼編碼表把編碼轉為字元串。
6. 在哈夫曼編碼壓縮程序中應該如何存儲哈夫曼編碼
我想你理解錯了……你所說的128Bit是編碼和解碼時用的
樹怎麼保存,方法很多,最簡單的辦法是為0x00到0xFF按它們出現的頻率保存順序;因為只有256個元素,所以用1位元組就能保存到它們的順序,例如
0x00 0x05
0x01 0x04
0x02 0xF5
……
……
要保存這種表示方式也只是要512位元組而已;重建樹的時候重再根據這些數據來把樹創建出來就是了;
最後建議你把哈夫曼編碼原理重新再看一次,是看「原理」
7. 哈夫曼壓縮字元文件的分隔符問題
哈夫曼 不需要分隔符 他沒有重碼
這里可以採用雙填充的手段:就是出現eof作為
數據部分的時候用eof eof 的格式讀的時候
出現連續的eof eof 時省略eof 保留一個作為數據!
8. 利用哈夫曼編碼進行壓縮壓縮率一般達到多少
哈夫曼編碼進行壓縮的壓縮率是根據平均碼長來計算的,壓縮率比較低。
例如:用三位二進行數進行的等長編碼平均長度為3,而根據哈夫曼樹編碼的平均碼長為:
4*0.07+2*0.19+5*0.02+4*0.06+2*0.32+5*0.03+2*0.21+4*0.10=2.61
2.61/3=0.87=87%
其平均碼長是等長碼的87%,所以平均壓縮率為13%。
哈夫曼編碼,又稱霍夫曼編碼,是一種編碼方式,哈夫曼編碼是可變字長編碼(VLC)的一種。
Huffman於1952年提出一種編碼方法,該方法完全依據字元出現概率來構造異字頭的平均長度最短的碼字,有時稱之為最佳編碼,一般就叫做Huffman編碼(有時也稱為霍夫曼編碼)。
壓縮率,描述壓縮文件的效果名,是文件壓縮後的大小與壓縮前的大小之比,例如:把100m的文件壓縮後是90m,壓縮率為90/100*100%=90%,壓縮率一般是越小越好,但是壓得越小,解壓時間越長。
(8)哈夫曼壓縮擴展閱讀
哈夫曼編碼的具體方法:先按出現的概率大小排隊,把兩個最小的概率相加,作為新的概率 和剩餘的概率重新排隊,再把最小的兩個概率相加,再重新排隊,直到最後變成1。
每次相 加時都將「0」和「1」賦與相加的兩個概率,讀出時由該符號開始一直走到最後的「1」, 將路線上所遇到的「0」和「1」按最低位到最高位的順序排好,就是該符號的哈夫曼編碼。