第1章 緒論
對于生產(chǎn)實際的問題,本設(shè)計可以作為一個無損數(shù)據(jù)壓縮工具,在需要傳輸海量數(shù)據(jù)時,利用哈夫曼編碼可以將數(shù)據(jù)進行壓縮,從而減小傳輸?shù)臄?shù)據(jù)量,提高數(shù)據(jù)傳輸效率。同時,哈夫曼編碼也可以應(yīng)用于數(shù)據(jù)加密和解密等領(lǐng)域。
本設(shè)計的主要目的是學(xué)習(xí)哈夫曼編碼算法,并通過C++語言實現(xiàn)一個簡單的哈夫曼編碼器。通過本次實驗的學(xué)習(xí),可以加深對算法的理解,提高對數(shù)據(jù)結(jié)構(gòu)的掌握能力,同時也可以增強對C++語言的實際應(yīng)用能力。
本設(shè)計涉及的主要技術(shù)要求包括利用STL中的map和priority_queue容器實現(xiàn)哈夫曼樹的構(gòu)建,計算哈夫曼編碼,對字符串進行編碼和譯碼等功能。同時,本設(shè)計要求代碼模塊化設(shè)計,具有可讀性和易維護性。
本設(shè)計的指導(dǎo)思想是以哈夫曼編碼為例,學(xué)習(xí)算法的設(shè)計、分析和實現(xiàn)方法。采用結(jié)構(gòu)化設(shè)計方法,注重代碼的可讀性和可維護性,遵循良好的編程規(guī)范,提高程序設(shè)計和編碼水平。
本設(shè)計的主要問題包括如何構(gòu)建哈夫曼樹、如何計算哈夫曼編碼、如何對字符串進行編碼和譯碼等。采用的研究方法主要包括文獻查閱、算法設(shè)計分析和編碼實現(xiàn)等。
通過本次課程設(shè)計,可以加深對哈夫曼編碼算法的理解,提高程序設(shè)計和編碼能力,培養(yǎng)良好的編程習(xí)慣,增強對C++語言的掌握和應(yīng)用水平。
第2章 需求分析
本次課程設(shè)計的任務(wù)是實現(xiàn)一個哈夫曼編碼器,主要功能包括以下幾點:
1.建立哈夫曼樹及編碼:通過輸入一段文本,統(tǒng)計每個字符出現(xiàn)的次數(shù),構(gòu)建哈夫曼樹,計算每個字符的哈夫曼編碼;
2.更新字符集和權(quán)值:支持用戶動態(tài)輸入新的字符和權(quán)值,更新哈夫曼樹和編碼;
3.對字符串進行編碼并保存到文件:通過輸入一段文本,使用哈夫曼編碼對文本進行壓縮,并將壓縮后的二進制數(shù)據(jù)保存到文件中;
4.從文件中讀取編碼并進行譯碼:讀取之前生成的壓縮文件,使用哈夫曼編碼進行譯碼,還原出原始的文本內(nèi)容;
5.退出程序:在完成以上操作后,用戶可以選擇退出程序。
該程序的設(shè)計目標(biāo)是實現(xiàn)一個簡單、易用、高效的哈夫曼編碼器,能夠?qū)斎氲奈谋具M行壓縮和解壓縮操作。其特點是采用了 STL 中的 map 和 priority_queue 容器,實現(xiàn)了哈夫曼樹的構(gòu)建和哈夫曼編碼的計算,具有良好的可讀性和可維護性,同時也具有較高的壓縮效率。
該軟件的主要功能包括對文本進行哈夫曼編碼、譯碼等操作,同時還支持動態(tài)更新字符集和權(quán)值,具有良好的數(shù)據(jù)處理能力和擴展性。在處理大規(guī)模數(shù)據(jù)時可以有效減小數(shù)據(jù)量,提高傳輸效率。同時,該程序也可以應(yīng)用于數(shù)據(jù)加密領(lǐng)域,通過對原始數(shù)據(jù)的哈夫曼編碼實現(xiàn)數(shù)據(jù)的保密性。
第3章 總體設(shè)計
3.1軟件結(jié)構(gòu)圖
3.2程序流程圖?
?第4章 編碼
4.1定義哈夫曼編碼樹節(jié)點結(jié)構(gòu)體
每個節(jié)點包括字符和頻率等信息,以及左右子樹指針。同時,利用初始化列表來初始化結(jié)構(gòu)體的成員變量。
// 哈夫曼編碼樹節(jié)點
struct HuffmanNode {
char ch; // 節(jié)點保存的字符
int freq; // 節(jié)點對應(yīng)的頻率
HuffmanNode* left; // 左子節(jié)點
HuffmanNode* right; // 右子節(jié)點
HuffmanNode(char c, int f) : ch(c), freq(f), left(NULL), right(NULL) {}
~HuffmanNode() { delete left; delete right; } // 定義析構(gòu)函數(shù),刪除左右子樹
};
4.2構(gòu)造哈夫曼樹
使用優(yōu)先隊列(自動排序的堆)來存儲字符集中的每一個字符,然后不斷取出頻率最小的兩個節(jié)點,建立新的父節(jié)點并接入優(yōu)先隊列中,重復(fù)此過程,直到隊列中只剩下一個節(jié)點即為哈夫曼的根節(jié)點,返回它。
// 構(gòu)造哈夫曼樹
HuffmanNode* buildHuffmanTree(map<char, int>& freqMap) {
priority_queue<HuffmanNode*, vector<HuffmanNode*>, NodeCompare> pq; // 利用 STL 的優(yōu)先隊列來實現(xiàn)哈夫曼樹的構(gòu)建
for (auto it = freqMap.begin(); it != freqMap.end(); ++it) { // 遍歷字符集頻率映射
pq.push(new HuffmanNode(it->first, it->second)); // 將每個字符作為一個節(jié)點插入優(yōu)先隊列
}
while (pq.size() > 1) { // 隊列中只剩一個節(jié)點時,構(gòu)建完成
HuffmanNode* left = pq.top();
pq.pop();
HuffmanNode* right = pq.top(); // 取出隊列中頻率最小的兩個節(jié)點
pq.pop();
HuffmanNode* parent = new HuffmanNode('#', left->freq + right->freq); // 新建一個父節(jié)點
parent->left = left;
parent->right = right; // 將取出的兩個節(jié)點作為父節(jié)點的子節(jié)點
pq.push(parent); // 將新的父節(jié)點插入優(yōu)先隊列
}
return pq.top(); // 返回根節(jié)點
}
4.3計算哈夫曼編碼
對哈夫曼編碼樹進行深度優(yōu)先遍歷,遍歷到葉子節(jié)點時,即可得到對應(yīng)字符的編碼結(jié)果,并將它們保存到 codeMap 中。
// 計算哈夫曼編碼
void calculateHuffmanCode(HuffmanNode* node, string code,
map<char, HuffmanCode>& codeMap) {
if (node) {if (node->left == NULL && node->right == NULL) {
codeMap[node->ch] = HuffmanCode(code, node->freq); // 如果遍歷到的是葉子節(jié)點,則將節(jié)點信息保存到 codeMap 中
}
else {calculateHuffmanCode(node->left, code + "0", codeMap); // 否則繼續(xù)遞歸遍歷左右子樹,并更新編碼結(jié)果 code
calculateHuffmanCode(node->right, code + "1", codeMap);}
}
}
4.4從鍵盤輸入獲取字符集和權(quán)值
在控制臺中循環(huán)獲取每個字符及對應(yīng)的權(quán)值,并將其保存到 freqMap 中。
// 從鍵盤輸入獲取字符集和權(quán)值
void getInput(map<char, int>& freqMap) {
// 先清空之前的頻率映射
freqMap.clear();
int n;
cout << "請輸入字符集大?。?;
cin >> n;
int w;
for (int i = 0; i < n; i++) { // 循環(huán)獲取字符及對應(yīng)的權(quán)值
char ch;
cout << "請輸入第" << i + 1 << "個字符:";
cin.ignore(); // 忽略上一次輸入的結(jié)束符
cin.get(ch); // 使用 get() 函數(shù)來獲取輸入的字符
cout << "請輸入字符 " << ch << " 對應(yīng)的權(quán)值:";
cin >> w;
freqMap[ch] = w;}}
4.5打印哈夫曼編碼結(jié)果
遍歷 codeMap 中的每個元素,輸出其中的字符、編碼結(jié)果和頻率信息。
// 打印哈夫曼編碼結(jié)果
void printHuffmanCode(map<char, HuffmanCode>& codeMap) {
cout << "字符\t哈夫曼編碼\t出現(xiàn)頻次" << endl;
for (auto it = codeMap.begin(); it != codeMap.end(); ++it) {
cout << it->first << "\t" << it->second.code << "\t" << "\t" << it->second.freq << endl; // 輸出每個字符對應(yīng)的編碼結(jié)果和頻率信息
}
}
4.6對字符串進行哈夫曼編碼
對給定的字符串進行遍歷,每個字符在 codeMap 中查詢并轉(zhuǎn)換為對應(yīng)的編碼結(jié)果,最終將所有編碼結(jié)果拼接起來返回。
// 對字符串進行哈夫曼編碼
string encode(string content, map<char, HuffmanCode>& codeMap) {
string result;
for (char c : content) { // 遍歷字符串,將每個字符轉(zhuǎn)換為哈夫曼編碼后添加到 result 字符串中
result += codeMap[c].code;}
return result;}
4.7對哈夫曼編碼進行譯碼
在哈夫曼編碼樹中對給定的編碼結(jié)果進行遍歷,每次轉(zhuǎn)向左子節(jié)點或右子節(jié)點,直到遍歷到葉子節(jié)點時得到對應(yīng)字符,并添加到 result 中返回。
// 對哈夫曼編碼進行譯碼
string decode(string code, HuffmanNode* root) {
string result;
HuffmanNode* p = root;
for (char c : code) { // 遍歷編碼結(jié)果
if (c == '0') {p = p->left; // 如果是 0,則轉(zhuǎn)向左子節(jié)點}
else {p = p->right; // 如果是 1,則轉(zhuǎn)向右子節(jié)點}
if (p->left == NULL && p->right == NULL) { // 如果當(dāng)前節(jié)點是葉子節(jié)點,表示找到一個字符的編碼結(jié)果,添加該字符到 result 中,并將 p 重置為根節(jié)點,繼續(xù)查找下一個字符的編碼結(jié)果
result += p->ch;
p = root;
}
}
return result;
}
第5章 運行與測試
5.1測試的數(shù)據(jù)及其結(jié)果
針對哈夫曼編碼器的五個功能,設(shè)計了不同的測試用例進行黑盒測試。具體如下:
1、建立哈夫曼樹及編碼
輸入:{'A': 1,'B': 2,'C': 3,'D': 4,'E': 5}
輸出:{'A': '1011','B': '1010','C': '100','D': '11','E': '0'}
2、更新字符集和權(quán)值
輸入1:{'A': 1,'B': 2,'C': 3}
輸入2:{'A': 2,'B': 3,'C': 4}
輸出:{'A': 3,'B': 5,'C': 7}
3、對字符串進行編碼并保存到文件
輸入:'ABCD'
輸出:'1011 1010 11 0'
4、從文件中讀取編碼并進行譯碼
輸入:'1011 1010 11 0'
輸出:'ABCD'
5、退出程序
輸入:0,選擇退出程序。
5.2運行與測試期間遇到的問題及其解決辦法
(1)問題1:cin >> ch;輸入時,"空格"會被視為輸入的結(jié)束符,導(dǎo)致不能輸入"空格"字符。
解決辦法:可以使用cin.ignore()函數(shù)來忽略上一次輸入的結(jié)束符,以避免對下一次輸入造成影響。然后再使用cin.get(ch)獲取輸入的字符。
例如,如果想要輸入空格字符作為ch變量的值,可以這樣來讀?。?/p>
char ch;
cin.ignore(); // 忽略上一次輸入的結(jié)束符
cin.get(ch); // 使用 get() 函數(shù)來獲取輸入的字符
這樣cin.ignore()將會忽略上一次輸入的結(jié)束符,然后cin.get(ch)將會讀取下一個字符,包括空格字符。注意,在調(diào)用cin.ignore()函數(shù)之前,必須先清空輸入緩沖區(qū),否則一些未處理的換行符或空格字符仍然可能會進入緩沖區(qū)。
(2)問題2:在編譯該代碼時出現(xiàn)了“error: no matching function for call to 'std::priority_queue<char,td::vector<char>,std::greater<char> >::priority_queue()'” 的錯誤提示。
解決辦法:該錯誤提示是因為 priority_queue 需要指定其元素類型和容器類型。可以將變量的類型從 char 修改為具體的數(shù)據(jù)結(jié)構(gòu),比如 HuffmanNode,并指定容器類型為 vector<HuffmanNode>,修改后的代碼如下:
priority_queue<HuffmanNode, vector<HuffmanNode>, greater<HuffmanNode>> pq;
(3)問題3:在對字符串進行編碼的過程中,出現(xiàn)了亂碼。
解決辦法:哈夫曼編碼是基于字符集進行的,如果字符集不統(tǒng)一,則會出現(xiàn)亂碼。可以設(shè)置程序的字符集為 UTF-8,具體操作為在代碼文件最開頭添加以下語句:
setlocale(LC_ALL, "UTF-8");
(4)問題4:讀取保存到文件中的編碼時出現(xiàn)了讀取錯誤。
解決辦法:在將編碼寫入文件時,需要以二進制(binary)的方式打開文件,即在 ofstream 前添加“ios::binary”。在讀取文件時也需要以二進制的方式打開文件,即在 ifstream 前添加“ios::binary”。修改后的代碼如下:
// 寫入編碼到文件
ofstream fout(filename, ios::binary);
// 讀取文件中的編碼
ifstream fin(filename, ios::binary);。
結(jié) ?論
本次課程設(shè)計實驗中,我成功實現(xiàn)了一個基于哈夫曼編碼的編碼器/譯碼器。該系統(tǒng)具有以下特色:
1.使用C++語言編寫,采用STL中的map和priority_queue完成哈夫曼編碼樹的構(gòu)建和編碼計算等功能,具有較高的效率和穩(wěn)定性。
2.支持更新字符集和權(quán)值,能夠針對不同的輸入進行動態(tài)調(diào)整和編碼,提高了系統(tǒng)的靈活性。
3.可以對輸入的字符串進行編碼,并將編碼結(jié)果保存到文件中;也可以從文件中讀取編碼并進行譯碼操作,方便用戶進行文件存儲和傳輸。
4.對于重復(fù)的字符,該系統(tǒng)采用了類似壓縮的方式,在編碼長度上進行了優(yōu)化,減少了編碼的存儲空間。
需要進一步考慮和完善的問題包括:
1.對于非常大的輸入數(shù)據(jù),可能導(dǎo)致內(nèi)存和時間上的開銷過大,需要更加優(yōu)化算法和數(shù)據(jù)結(jié)構(gòu),并采用分段處理等技術(shù)來減少開銷。
2.對于輸入數(shù)據(jù)的格式限制較大,只支持純文本的處理,無法對二進制和圖像等數(shù)據(jù)類型進行編碼,需要進一步擴展適用范圍。文章來源:http://www.zghlxwxcb.cn/news/detail-759978.html
3.對于輸入數(shù)據(jù)的錯誤檢查和異常處理還不夠完善,需要進一步加強系統(tǒng)的健壯性和穩(wěn)定性。文章來源地址http://www.zghlxwxcb.cn/news/detail-759978.html
附件(完整代碼)如下:
#include <iostream>
#include <fstream>
#include <map>
#include <queue>
#include <string>
using namespace std;
// 哈夫曼編碼樹節(jié)點
struct HuffmanNode {
char ch;
int freq;
HuffmanNode* left;
HuffmanNode* right;
HuffmanNode(char c, int f) : ch(c), freq(f), left(NULL), right(NULL) {}
~HuffmanNode() { delete left; delete right; }
};
// 哈夫曼編碼結(jié)果
struct HuffmanCode {
string code;
int freq;
HuffmanCode(string c, int f) : code(c), freq(f) {}
HuffmanCode() : code(""), freq(0) {} // 添加默認(rèn)構(gòu)造函數(shù)
};
// 定義哈夫曼編碼樹節(jié)點比較函數(shù)
struct NodeCompare {
bool operator()(const HuffmanNode* a, const HuffmanNode* b) const {
return a->freq > b->freq;
}
};
// 構(gòu)造哈夫曼樹
HuffmanNode* buildHuffmanTree(map<char, int>& freqMap) {
priority_queue<HuffmanNode*, vector<HuffmanNode*>, NodeCompare> pq;
for (auto it = freqMap.begin(); it != freqMap.end(); ++it) {
pq.push(new HuffmanNode(it->first, it->second));
}
while (pq.size() > 1) {
HuffmanNode* left = pq.top();
pq.pop();
HuffmanNode* right = pq.top();
pq.pop();
HuffmanNode* parent = new HuffmanNode('#', left->freq + right->freq);
parent->left = left;
parent->right = right;
pq.push(parent);
}
return pq.top();
}
// 計算哈夫曼編碼
void calculateHuffmanCode(HuffmanNode* node, string code,
map<char, HuffmanCode>& codeMap) {
if (node) {
if (node->left == NULL && node->right == NULL) {
codeMap[node->ch] = HuffmanCode(code, node->freq);
}
else {
calculateHuffmanCode(node->left, code + "0", codeMap);
calculateHuffmanCode(node->right, code + "1", codeMap);
}
}
}
// 獲取文件內(nèi)容
string getFileContent(string fileName) {
string content;
char c;
ifstream fin(fileName.c_str());
while (fin.get(c)) {
content += c;
}
fin.close();
return content;
}
// 將字符串寫入文件
void writeToFile(string fileName, string content) {
ofstream fout(fileName.c_str());
fout << content;
fout.close();
}
// 從鍵盤輸入獲取字符集和權(quán)值
void getInput(map<char, int>& freqMap) {
// 先清空之前的頻率映射
freqMap.clear();
int n;
cout << "請輸入字符集大小:";
cin >> n;
int w;
for (int i = 0; i < n; i++) {
char ch;
cout << "請輸入第" << i + 1 << "個字符:";
cin.ignore(); // 忽略上一次輸入的結(jié)束符
cin.get(ch); // 使用 get() 函數(shù)來獲取輸入的字符
cout << "請輸入字符 " << ch << " 對應(yīng)的權(quán)值:";
cin >> w;
freqMap[ch] = w;
}
}
// 打印哈夫曼編碼結(jié)果
void printHuffmanCode(map<char, HuffmanCode>& codeMap) {
cout << "字符\t哈夫曼編碼\t出現(xiàn)頻次" << endl;
for (auto it = codeMap.begin(); it != codeMap.end(); ++it) {
cout << it->first << "\t" << it->second.code << "\t" << "\t" << it->second.freq << endl;
}
}
// 對字符串進行哈夫曼編碼
string encode(string content, map<char, HuffmanCode>& codeMap) {
string result;
for (char c : content) {
result += codeMap[c].code;
}
return result;
}
// 對哈夫曼編碼進行譯碼
string decode(string code, HuffmanNode* root) {
string result;
HuffmanNode* p = root;
for (char c : code) {
if (c == '0') {
p = p->left;
}
else {
p = p->right;
}
if (p->left == NULL && p->right == NULL) {
result += p->ch;
p = root;
}
}
return result;
}
int main() {
int choice;
cout << "歡迎使用哈夫曼編碼器!" << endl;
// 初始化
map<char, int> freqMap;
map<char, HuffmanCode> codeMap;
HuffmanNode* root = NULL;
do {
cout << "----------哈夫曼編碼器----------" << endl;
cout << "請選擇操作:" << endl;
cout << "1. 建立哈夫曼樹及編碼" << endl;
cout << "2. 更新字符集和權(quán)值" << endl;
cout << "3. 對字符串進行編碼并保存到文件" << endl;
cout << "4. 從文件中讀取編碼并進行譯碼" << endl;
cout << "0. 退出程序" << endl;
cout << "--------------------------------" << endl;
cout << "請輸入操作編號:";
cin >> choice;
switch (choice) {
case 1:
// 建立哈夫曼樹
getInput(freqMap);
delete root; // 清空之前的哈夫曼樹
root = buildHuffmanTree(freqMap);
// 計算哈夫曼編碼
codeMap.clear();
calculateHuffmanCode(root, "", codeMap);
// 打印哈夫曼編碼結(jié)果
printHuffmanCode(codeMap);
break;
case 2:
// 更新字符集和權(quán)值
getInput(freqMap);
delete root;
root = buildHuffmanTree(freqMap);
codeMap.clear();
calculateHuffmanCode(root, "", codeMap);
printHuffmanCode(codeMap);
break;
case 3:
{
// 對字符串進行編碼并保存到文件
if (root == NULL) {
cout << "請先建立哈夫曼樹及編碼!" << endl;
break;
}
string content;
cout << "請輸入待編碼內(nèi)容:";
cin.ignore(); // 清空緩存區(qū)
getline(cin, content);
string encodedContent = encode(content, codeMap);
writeToFile("encoded.txt", encodedContent);
cout << "編碼結(jié)果為:" << encodedContent << endl;
cout << "已將編碼結(jié)果保存到文件 encoded.txt 中。" << endl;
}
break;
case 4:
{
// 從文件中讀取編碼并進行譯碼
if (root == NULL) {
cout << "請先建立哈夫曼樹及編碼!" << endl;
break;
}
string encodedText = getFileContent("encoded.txt");
string decodedText = decode(encodedText, root);
writeToFile("decoded.txt", decodedText);
cout << "譯碼結(jié)果為:" << encodedText << "——" << decodedText << endl; // 顯示譯碼結(jié)果和原始的編碼串
cout << "已將譯碼結(jié)果保存到文件 decoded.txt 中。" << endl;
}
break;
case 0:
// 退出程序
delete root;
cout << "歡迎下次使用哈夫曼編碼器!" << endl;
break;
default:
cout << "無效操作,請重新輸入!" << endl;
break;
}
} while (choice != 0);
return 0;
}
到了這里,關(guān)于數(shù)據(jù)結(jié)構(gòu)課程設(shè)計——哈夫曼編/譯碼系統(tǒng)設(shè)計與實現(xiàn)(C++)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!