哈夫曼編碼和譯碼&&c++重點知識的應用
哈夫曼編碼和譯碼。將以前學的c++相關知識系統的用了一遍,反正是能想到啥,啥方便就用啥,但是說回來,也沒省多少事。反正比用c語言寫跟簡單一點。
先看題目原型吧!
假設某通訊報文的字符集由A,B,C,D,E,F這6個字元組成,它們在報文中出現的頻度(頻度均為整數值)。 (1)構造一棵哈弗曼樹,依次給出各字元編碼結果。 (2)給字串進行編碼。 (3)給編碼串進行譯碼。 規定: 構建哈弗曼樹時:左子樹根結點權值小於等於右子樹根結點權值。 生成編碼時:左分支標0,右分支標1。 輸入 第一行:依次輸入6個整數,依次代表A,B,C,D,E,F的頻度,用空格隔開。 第二行:待編碼的字串 第三行:待譯碼的編碼串 輸出 前6行依次輸出各個字元及其對應編碼,格式為【字元:編碼】(冒號均為英文符號) 第7行:編碼串 第8行:譯碼串 樣例輸入 3 4 10 8 6 5 BEE 0010000100111101 樣例輸出 A:000 B:001 C:10 D:01 E:111 F:110 001111111 BADBED
所用到的相關知識,最近學的智慧指標,迭代器,STL中的list連結串列,array陣列容器,繼承,就這些吧!我也不怎麼考慮演算法的優劣了。這感覺確實所用到的變數太多了。我得彙總一下~~~
以上為相關類和屬性,其中data類中
- a 字元出現的頻數
- ch 對應的字元
- s 編碼連結串列字元型別,左為0右為1
- hafu類中的方法全是靜態方法(通過類名來使用)
其中hafu類是繼承data類的。tag屬性是標記當前節點是否存在父節點,不存在為0,存在為l、p、r分別為左孩子下標有孩子下標,p為雙親下標。
首先初始化data連結串列,將字元該放入的放入,該置0的置0,接下來第一核心部分建樹。
難就難在找最小權值下標和次小權值下標,不管了,我的實現方法就是先在無父節點的節點中找最大值,並在findmax函式中記錄無父節點的節點個數,當個數為1時說明我們的樹已經建的差不多了,該收工了,否則,設定min等於那個最大值+1,然後在陣列中和其他節點權值比較,依據題目要求,讓左節點權值小於有節點權值,先獲得的最小權值的下標賦給kl,在第一趟迴圈完了後將以kl為下標的節點的tag置為1,它將是有爸爸的孩子啦!然後再在第二趟迴圈中招最小權值下標,賦給kr,然後就是哈夫曼演算法,讓父節點的做有孩子下標為kl,kr,讓孩子的父親節點的下標為新建立的節點的下標!樹的建立大致思路就是這樣。點到為止!
接下來就是編碼,這在哈夫曼樹演算法中不算核心,很簡單,就根據節點的個數,逐個遍歷,從第一個開始,按照父親節點的下標,然後依據父親節點的下標找到父親節點,然後,看父親節點的孩子下標,判斷當前節點的下標是父節點的左孩子還是右孩子,若是右孩子,往編碼連結串列中追加字元1,否則追加字元0,當遇到當前節點的父節點下標為0時,表示已經到樹頂了!然後將連結串列逆置,因為我們的編碼方向是從樹頂到根,逆置後才是當前節點真正的哈弗曼編碼,直接呼叫函式就行了。然後進行下一個節點的編碼!同樣的方法!
在我看來,最難得就是譯碼了,真是很燒腦。
我是怎麼解決的呢?
方法比較笨!根據輸入的編碼陣列,我們要在指標陣列中根據不同字元的編碼和輸入的字串進行匹配,然後找到匹配的字元輸出。我就是先遍歷物件陣列,用每一個字元的編碼與所輸入的陣列前面的字元進行匹配,若匹配正確就將輸入的陣列中的剛匹配的字元全置為‘#’符號,然後再輸出物件陣列中存的對應字元,再從物件陣列頭開始和匹配陣列非‘#’元素再進行匹配
main函式
#include<iostream>
#include<stdlib.h>
#include<memory>
#include<list>
#include"hafuman.h"
using namespace std;
int main(){
//智慧指標陣列
array<shared_ptr<hafu>,SIZE>arr ;
//初始化
hafu::init(arr);
//建立哈夫曼樹
hafu::process(arr);
//獲取哈夫曼編碼
hafu::makeCode(arr);
//返回哈夫曼編碼
hafu::request(arr);
//翻譯編碼
hafu::transform(arr);
}
標頭檔案
#ifndef _HAFU_H_
#define _HAFU_H_
#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<memory>
#include<vector>
#include<list>
#include<string.h>
using namespace std;
//SIZE為字元個數
//MAXLINE為使用的字串長度
enum {SIZE = 11,MAXLINE = 100};
class data{
public:
int a ;
char ch ;
list<char>s;
};
class hafu:public data{
public:
data da;
int tag ;
int l,p,r;
hafu():tag(0){}
~hafu(){}
//找當前陣列中無父節點的節點中字元頻率(權值)最大的節點
static int findmax(int s,int &kl,array<shared_ptr<hafu>,SIZE>&ls);
//找當前無父節點的節點中權值最小的兩個節點
static void find(int s,int& kl,int &kr,array<shared_ptr<hafu>,SIZE>&ls);
//初始化智慧指標陣列
static void init(array<shared_ptr<hafu>,SIZE>&ls);
//建立哈夫曼樹
static void process(array<shared_ptr<hafu>,SIZE>&ls);
//便利哈夫曼樹
static void print(const array<shared_ptr<hafu>,SIZE>&ls);
//編碼
static void makeCode(array<shared_ptr<hafu>,SIZE>&ls);
//逆置連結串列
static void reverse(list<char>&s);
//列印各個的編碼
static void printCodes(array<shared_ptr<hafu>,SIZE>&ls);
//輸入字元,返回相關編碼
static void request(const array<shared_ptr<hafu>,SIZE>&ls);
//列印各自父節點的編碼
static void printlist( list<char>&s);
//譯碼
static void transform(const array<shared_ptr<hafu>,SIZE>&ls);
static int search(list<char>&s ,char* arr,int i);
};
int hafu::search(list<char>&s,char *arr,int i){
list<char>::iterator iter ;
int k = 0 ;
for(k = 0 ;k < strlen(arr);k++ ){
if(arr[k]!='#')break ;
}
int m = k ;
int count = 0 ;
for(iter = s.begin();iter!=s.end();iter++){
if(*iter == arr[k]){
count ++ ;
k++ ;
}
else{
return 0;
}
}
int len = s.size();
int kk = m;
for(k = 0 ;k< len ;k++){
kk = m+k;
arr[kk] ='#';
}
return 1 ;
}
void hafu::transform(const array<shared_ptr<hafu>,SIZE>&ls){
char s[MAXLINE];
cin>>s;
int i = 0,j ;
int len = strlen(s);
for(i =0 ;i< (SIZE+1)/2;i++){
i = j ;
if(s[strlen(s)-1]=='#'){
break;
}
if(search(ls[i]->da.s,s,i)){
cout<<ls[i]->da.ch<<"";
j = 0 ;
}
else{
j++ ;
}
}
cout<<endl;
}
void hafu::printlist( list<char>&s){
list<char>::iterator iter ;
for(iter=s.begin();iter!=s.end() ;iter++){
cout<<*iter<<"";
}
}
void hafu::request(const array<shared_ptr<hafu>,SIZE>&ls){
char arr[MAXLINE];
cin>>arr ;
int len = strlen(arr);
int i ,j;
int flag = 0 ;
for(i = 0 ;i< len ;i++){
for(j = 0 ;j<(SIZE+1)/2;j++){
if(ls[j]->da.ch == arr[i]){
printlist(ls[j]->da.s);
flag =1 ;
}
}
if(flag==0)return ;
}
}
void hafu::printCodes(array<shared_ptr<hafu>,SIZE>&ls){
int i = 0;
for(i = 0 ; i < (SIZE+1)/2 ;i++){
list<char>::iterator iter ;
cout<<ls[i]->da.ch<<":";
for(iter = ls[i]->da.s.begin() ; iter!=ls[i]->da.s.end();iter++){
cout<<*iter<<"";
}
cout<<endl ;
}
}
void hafu:: reverse(list<char>&s){
int i ;
s.reverse();
}
void hafu::makeCode(array<shared_ptr<hafu>,SIZE>&ls){
int i = 0 ;
int j = 0;
for(j = 0 ;j < (SIZE+1)/2;j++){
int index = ls[j]->p;
int k = j ;
while(index){
if(ls[index]->l == k){
ls[j]->da.s.push_back('0');
}
if(ls[index]->r == k){
ls[j]->da.s.push_back('1');
}
k = index ;
index = ls[index]->p;
}
reverse(ls[j]->da.s);
}
printCodes(ls);
}
int hafu::findmax(int s,int &kl,array<shared_ptr<hafu>,SIZE>&ls){
int i ,j;
int temp ;
kl = -1 ;
int count = 0;
for(i = 0;i < s ;i++){
if(kl< ls[i]->da.a && ls[i]->tag != 1){
kl = i;
}
if(ls[i]->tag == 0)count++ ;
}
}
void hafu::find(int s,int& kl,int &kr,array<shared_ptr<hafu>,SIZE>&ls){
int i= 0, j = 0 ;
int count ;
count = findmax(s,kl,ls);
if(count==1){
kr = -1 ;
return ;
}
int mins =ls[kl]->da.a+1;
for(i = 0 ;i < 2 ;i++){
int min = mins ;
for(j = 0 ; j < s ; j++){
if(i==0&&ls[j]->da.a < min&&ls[j]->tag == 0){
min = ls[j]->da.a ;
kl = j ;
}
if(i==1&&ls[j]->da.a < min&& ls[j]->tag==0){
min = ls[j]->da.a ;
kr = j;
}
}
if(i == 0)ls[kl]->tag = 1 ;
if(i == 1){
ls[kr]->tag = 1 ;
break ;
}
}
}
void hafu::print(const array<shared_ptr<hafu>,SIZE>&ls){
int i = 0 ;
for(i =0 ;i < SIZE ;i++){
cout<<ls[i]->da.a<<" "<<ls[i]->p<<" "<<ls[i]->l<<" "<<ls[i]->r<<endl;
}
}
void hafu::process(array<shared_ptr<hafu>,SIZE>&ls){
int j = (SIZE+1)/2;
int i ;
int kl=-1,kr=-1 ;
for(i =j ;i< SIZE;i++){
find(i,kl,kr,ls);
if(kr == -1)break ;
ls[i]->da.a = ls[kr]->da.a + ls[kl]->da.a ;
ls[i]->l = kl ;
ls[i]->r = kr ;
ls[kl]->p = i ;
ls[kr]->p = i ;
}
}
void hafu::init(array<shared_ptr<hafu>,SIZE>&ls){
int j = 0 ;
for(j = 0;j < SIZE;j++){
shared_ptr<hafu>p(new hafu);
if(j<6)cin>>p->da.a ;
ls[j] = p ;
}
int i = 0;
char ch;
char arr[6] = {'A','B','C','D','E','F'};
for(i = 0 ;i< (SIZE+1)/2;i++){
ls[i]->da.ch=arr[i];
}
i = 0 ;
while(1){
if(i == SIZE){
break ;
}
ls[i]->l = 0;
ls[i]->r = 0;
ls[i]->p = 0;
if(i >= (SIZE+1)/2){
ls[i]->da.ch = '\0';
ls[i]->da.a = 0;
i