哈夫曼編碼 數據結構

上週日做了數據結構實驗報告,感覺還行。

 

 

一、課題描述


  編制一個運用哈夫曼編碼的相關知識對任意文本文件進行編碼、解碼,並保存相關結果的程序。

 

二、概要設計(主要思想應根據代碼執行順序瞭解)


1)數據邏輯結構:主要是樹形結構,也有使用線性結構作爲輔助;
存儲結構分析:主要是非線性結構(二叉樹)。
   結點:struct HuffTree{
      char data;
      int weight;
      HuffTree*left=NULL, *right=NULL;
      };
2)本程序包含8個函數:
(1)將數據樹化函數HuffTree* create(const char c, int i)
 c爲字符,i爲權值,將其存入一棵最小的二叉樹中,左右兒子均爲空,然後返回這棵二叉樹,配合init()函數使用。
(2)初始化函數void init():
 接受輸入的字符及其權值,並將每一組字符和權值樹化,存入vector<HuffTree>容器Huff中。

(3)比較函數bool cmp(HuffTree*h1,HuffTree*h2)
 比較兩個結點,如果前者權值小於後者,返回TRUE,否則返回FALSE,輔助sort函數使用。

(4)合併兩棵樹函數HuffTree* merge(HuffTree*t1, HuffTree*t2)
 創建一個新結點,權值爲t1和t2的權值之和,char數據爲’#’,表示非輸入的字符,然後返回這個結點(樹)。
 
(5)(主要算法實現)編碼函數HuffTree* Encode()
 在容器Huff裏面,只要不止一棵樹,就進行排序,將權值小的排在前面,按規則合併最小的兩棵樹(刪除原兩樹,將新樹插入),當還剩下一棵樹的時候,這棵樹就是哈夫曼樹,然後把這棵樹返回,用FinalTree接收。

(6)得到編碼函數void HuffVisit(HuffTree*t, string s)
 遍歷FinalTree,以左爲1,右爲0,每碰到data爲字母的子樹,就把當前的01字符和字母串存入容器HuffDic中(以字母爲key)

(7)譯碼函數void Decode(HuffTree*t, string s)
 依s中01字符串以1左0右的規則遍歷FinalTree,得到的每一個字母均存入string decStr中,得到譯碼字符串decStr。

(8)文件操作函數void file()
 將HuffDic和decStr中存儲的結果寫入文件中。

(9)展示函數void show()
 將測試結果打印出來

(10)主函數int main()
 調用其他函數,完成功能。

 

 

三、代碼編寫(注:本代碼在Visual Studio 2017下編譯運行通過)

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<cctype>
#include<fstream>
using namespace std;

struct HuffTree			//樹結點結構體
{
	char data;				//數據:字符
	int weight;				//權值
	HuffTree*left = NULL, *right = NULL;	//左右子樹
};


typedef pair<char, string> Pair;	//爲了方便起見,現將前者簡化寫法
bool space=false;
HuffTree* FinalTree;			//最終的哈夫曼樹
vector<Pair> HuffDic;			//存放每個字符的哈夫曼編碼
vector<HuffTree*> Huff;			//存放開始的全部哈夫曼樹
string DecodeStr;				//對01字符串解碼後的字符串

HuffTree* CreateTree(const char c, int i)	//將數據化爲小樹
{
	HuffTree*t = new HuffTree;
	t->data = c;
	t->weight = i;
	return t;			//返回小樹
}

void Initial()
{
	int n, t;
	char c;
	cout << "請輸入數據組數:";				//數據組數:欲編碼字母個數
	cin >> n;
	while (n--)
	{
		static int k = 1;
		cout << "第" << k++ << "組(字母,權值):";
		cin >> c >> t;
		Huff.push_back(CreateTree(c, t));		//將小樹存Huff中去
	}
	cout << "是否輸入空格' '的權值?如果是,請輸入正整數;如果否,請輸入負數:";			//確認是否爲空格編碼
	cin >> t;
	if (t >= 0)
	{
		Huff.push_back(CreateTree(' ', t)); 
		space = true;
	}
	
}

bool SortCmp(HuffTree*h1, HuffTree*h2)		//sort的比較函數重載
{
	return h1->weight < h2->weight;			//按權值升序
}


HuffTree* TreeMerge(HuffTree*t1, HuffTree*t2)		//和並兩棵小樹,使兩棵小樹成爲大樹的孩子
{
	HuffTree*t = new HuffTree;
	t->data = '#';							//以#號表示無字符
	t->weight = t1->weight + t2->weight;		//大樹的權值是兩棵小樹權值之和
	t->left = t1;
	t->right = t2;
	return t;
}

HuffTree* GetFinalTree()
{
	//for (int i = 0; i < Huff.size(); i++)
	//{
	//cout << Huff[i]->weight << ' ' << Huff[i]->data << endl;
	//}
	while (Huff.size() > 1)				//Huff內不止一棵樹的時候
	{
		sort(Huff.begin(), Huff.end(), SortCmp);	//把最小的樹排序到前面
		HuffTree*t1, *t2;
		t1 = Huff.front();
		Huff.erase(Huff.begin());
		t2 = Huff.front();
		Huff.erase(Huff.begin());
		Huff.push_back(TreeMerge(t1, t2));		//合併最小的兩棵樹
	}
	return Huff.front();				//返回最後剩餘的一棵樹,即FinalTree
}

void Encode(HuffTree*t, string s)
{
	//if (isalpha(t->data))
	if (t->data != '#')
		HuffDic.push_back(Pair(t->data, s));		//如果字符不是#,則和相應的01字符串一起存入HuffDic中
	if (t->left)						//如果有左子樹
	{
		string sl = s;
		sl.push_back('0');					//左邊爲1,放入string
		Encode(t->left, sl);				//遞歸
	}
	if (t->right)					//如果有右子樹
	{
		string sr = s;
		sr.push_back('1');					//右邊爲0,放入string
		Encode(t->right, sr);				//遞歸
	}
}

void Decode(HuffTree*t, string s)
{
	if (t->left == NULL || s.empty())			//當沒有孩子或01字符串完了的時候
	{
		DecodeStr.push_back(t->data);				//爲相應的字母,取出放到DecodeStr中
		if (!s.empty())
			Decode(FinalTree, s);			//如果s還不爲空的話,繼續下一個字符的解碼
	}
	else
	{
		char front = s.front();					//有孩子且還有01字符串時,去掉首字符,繼續遍歷
		s.erase(s.begin());
		//首字符爲0向左,首字符爲1向右
		Decode((front == '0' ? t->left : t->right), s);
	}

}

void FileAction()
{
	fstream f("D:\\test.txt", ios::out);			//打開文件,若不存在便創建
	if (!f)
	{
		cout << "FileError!! Can't read and write file 'test.txt'!!";	//打不開文件即退出函數
		return;
	}
	for (int i = 0; i < HuffDic.size(); i++)
	{
		f << HuffDic[i].first << "——" << HuffDic[i].second << endl;		//將HuffDic中保存的字母及其編碼寫入文件
	}
	f << DecodeStr;						//將DecodeStr寫入文件
	f.close();
	cout << "結果已保存至——'test.txt'!" << endl;
}

void Results()
{
	sort(HuffDic.begin(), HuffDic.end());					//將小的字符排在前,進行打印
	cout << "哈夫曼編碼:" << endl;
	for (int i = 0; i < HuffDic.size(); i++)
	{
		cout << HuffDic[i].first << "——" << HuffDic[i].second << endl;	//輸出格式
	}
	cout << "請輸入欲編碼的字符串:";
	//cin >> test;
	char c[100] = { 0 };
	getchar();
	cin.getline(c, 100);				//getline以輸入帶空格的字符串
	string test(c, c + 100);
	int sym = 0;
	for (string::iterator it = test.begin(); it != test.end(); it++)
	{
		sym = 0;
		for (vector<Pair>::iterator i = HuffDic.begin(); i != HuffDic.end()&!sym; i++)
			if (i->first == *it) sym = 1;			//在HuffDic中是否有對應的編碼
		if (sym)			//如果有
		{
			if (*it == ' ')
				cout << HuffDic.front().second;		//如果是空格
			else if (*it)
				cout << HuffDic[*it - 65 + int(space)].second;			//輸出測試數據中每個字母的哈夫曼編碼
		}
		//else
		//{
		//	cout << "字符串中有未編碼字符,編碼停止,你可以檢查並再次運行.";
		//	system("pause");
		//	exit(0);
		//}
	}
	if (sym)
		cout << "字符串中有未編碼字符,編碼結果可能錯誤";
	cout << endl << "請輸入欲解碼的01編碼:"<<endl;
	cin >> test;
	Decode(FinalTree, test);				//輸入編碼,進行解碼
	cout << DecodeStr << endl;
}

int main()
{
	Initial();
	FinalTree = GetFinalTree();
	Encode(FinalTree, "");
	Results();
	FileAction();
	return 0;
}

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章