生活随笔
收集整理的這篇文章主要介紹了
数据结构——哈弗曼编码问题
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
實(shí)驗(yàn)六 基于哈夫曼樹(shù)的數(shù)據(jù)壓縮算法 【實(shí)驗(yàn)?zāi)康摹?/p>
掌握哈夫曼樹(shù)的構(gòu)造算法。 掌握哈夫曼編碼的構(gòu)造算法。 【實(shí)驗(yàn)內(nèi)容】 問(wèn)題描述 輸入一串字符,根據(jù)給定的字符串中字符出現(xiàn)的頻率建立相應(yīng)的哈夫曼樹(shù), 構(gòu)造哈夫曼編碼表,在此基礎(chǔ)上可以對(duì)壓縮文件進(jìn)行壓縮(即編碼),同時(shí)可以對(duì) 壓縮后的二進(jìn)制編碼文件進(jìn)行解壓(即譯碼)。 輸入要求 多組數(shù)據(jù),每組數(shù)據(jù) 1 行,為一個(gè)字符串(只考慮 26 個(gè)小寫(xiě)字母即可)。當(dāng)輸 入字符串為“00”時(shí),輸入結(jié)束。 輸出要求 每組數(shù)據(jù)輸出 2n+3 行(n 為輸入串中字符類別的個(gè)數(shù))。第 1 行為統(tǒng)計(jì)出來(lái) 的字符出現(xiàn)頻率(只輸出存在的字符,格式為字符:頻度),每?jī)山M字符之間用一個(gè) 空格分隔,字符按照 ASCII 碼從小到大的順序排列。第 2 行至第 2n 行為哈夫曼樹(shù) 的存儲(chǔ)結(jié)構(gòu)的終態(tài)(參照實(shí)驗(yàn)提示表 5.2,一行當(dāng)中的數(shù)據(jù)用空格分隔)。第 2n+1 行為每個(gè)字符的哈夫曼編碼(只輸出存在的字符,格式為字符:編碼),每?jī)山M字符 之間用一個(gè)空格分隔,字符按照 ASCII 碼從小到大的順序排列。第 2n+2 行為編碼 后的字符串,第 2n+3 行為解碼后的字符串(與輸入的字符串相同)。 輸入樣例 aaaaaaabbbbbccdddd aaccb 00 輸出樣例 a:7 b:5 c2 d:4 1 7 7 0 0 2 5 6 0 0 3 2 5 0 0 4 4 5 0 0 5 6 6 3 4 6 1 1 7 2 5 7 1 8 0 1 6 a:0 b:10 c:110 d:111 00000001010101011011011111 aaaaaaabbbbbccdddd a:2 b:1 c:3 1 2 4 0 0 2 1 4 0 0 3 3 5 0 0 4 3 5 2 1 5 6 0 3 4 a:11 b:10 c:0 111110000 aabccc 【實(shí)驗(yàn)提示】 首先,讀入一行字符串,統(tǒng)計(jì)每個(gè)字符出現(xiàn)的頻率;然后,根據(jù)字符出現(xiàn)的頻 率利用提示算法 1 建立相應(yīng)的哈夫曼樹(shù);最后,根據(jù)得到的哈夫曼樹(shù)利用算法 2 求出每個(gè)字符的哈夫曼編碼。
思路:
該問(wèn)題使用順序表來(lái)存儲(chǔ)哈夫曼樹(shù)
char_statiscal()函數(shù)統(tǒng)計(jì)每種字符出現(xiàn)的次數(shù)
CreatHuffmanCode()構(gòu)建哈弗曼樹(shù)數(shù)組,共有n個(gè)葉子結(jié)點(diǎn),n-1個(gè)非葉子結(jié)點(diǎn)。
select()函數(shù)選取哈弗曼數(shù)組中權(quán)值最小的兩個(gè)葉子結(jié)點(diǎn),返回到CreatHuffmanCode()函數(shù)中,用于完成對(duì)非葉子結(jié)點(diǎn)的構(gòu)建 并且同時(shí)修改葉子結(jié)點(diǎn)的父親結(jié)點(diǎn),和非葉子結(jié)點(diǎn)的孩子結(jié)點(diǎn)
結(jié)構(gòu)體 typedef struct { char info;//存儲(chǔ)每一個(gè)結(jié)點(diǎn)對(duì)應(yīng)的字符 int weight;//權(quán)值 int parent,lchild,rchild; int index;//存儲(chǔ)每一個(gè)結(jié)點(diǎn)對(duì)應(yīng)的下標(biāo) char code[MAXCODE];//存儲(chǔ)每一個(gè)字符對(duì)應(yīng)的二進(jìn)制編碼
}HTNode,*HuffmanTree;
6.CreatHuffmanCode()函數(shù)為每種字符創(chuàng)建各自的二進(jìn)制編碼
encode()將每個(gè)字符的編碼放到該葉子結(jié)點(diǎn)的code[] 中去。 具體過(guò)程:依次讀入字符,在哈弗曼編碼表(數(shù)組中)找到次字符,將字符轉(zhuǎn)換為編碼表中存放的編碼串。
decode()利用以構(gòu)建好的哈弗曼樹(shù)來(lái)進(jìn)行解碼 對(duì)編碼后的文件進(jìn)行譯碼的過(guò)程必須借助于哈弗曼樹(shù)。 具體過(guò)程:依次讀入文件的二進(jìn)制碼,從哈弗曼樹(shù)的根節(jié)點(diǎn)出發(fā),若是0,則走向左子樹(shù),否則走向右子樹(shù)。一旦到達(dá)葉子結(jié)點(diǎn),便譯出相應(yīng)的字符編碼。然后繼續(xù)從根節(jié)點(diǎn)出發(fā)繼續(xù)譯碼,直到全部結(jié)束。
# include <stdio.h>
# include <iostream>
# include <string.h>
using namespace std
;
# define MAXSTRLEN 255
# define MAXCODE 20 char alphabet
[ 26 ] = { 0 } ;
char alphabet2
[ 26 ] ;
typedef char * * HuffmanCode
;
typedef struct
{ char String
[ MAXSTRLEN
] ;
} SqString
; typedef struct
{ char info
; int weight
; int parent
, lchild
, rchild
; int index
; char code
[ MAXCODE
] ; } HTNode
, * HuffmanTree
;
void char_statiscal ( SqString
& S
, char * alphabet
, int & num
, char * alphabet2
)
{ int i
= 0 , j
= 0 ; for ( i
= 0 ; S
. String
[ i
] != '\0' ; i
++ ) { if ( S
. String
[ i
] >= 'a' && S
. String
[ i
] <= 'z' ) alphabet
[ S
. String
[ i
] - 'a' ] ++ ; } for ( int j
= 0 ; j
< 26 ; j
++ ) { if ( alphabet
[ j
] > 0 ) { printf ( "%c:%d " , 'a' + j
, alphabet
[ j
] ) ; num
++ ; } } printf ( "\n" ) ; int x
= 0 ; for ( int j
= 0 ; j
< 26 ; j
++ ) { if ( alphabet
[ j
] > 0 ) { alphabet2
[ x
] = 'a' + j
; x
++ ; } } }
void createHuffmanTree2 ( HuffmanTree
& HT
, int n
, char * alphabet
, char * alphabet2
)
{ if ( n
< 1 ) return ; int m
= 2 * n
- 1 ; int number
= 0 ; HT
= new HTNode
[ m
+ 1 ] ; for ( int i
= 1 ; i
<= n
; i
++ ) { HT
[ i
] . parent
= 0 ; HT
[ i
] . lchild
= 0 ; HT
[ i
] . rchild
= 0 ; HT
[ i
] . index
= i
; } for ( int j
= 0 ; j
< 26 ; j
++ ) { if ( alphabet
[ j
] > 0 ) HT
[ ++ number
] . weight
= 0 ; } }
void select ( HuffmanTree
& HT
, int length
, int & s1
, int & s2
)
{ HuffmanTree ht
; createHuffmanTree2 ( ht
, length
, alphabet
, alphabet2
) ; for ( int i
= 1 ; i
<= length
; i
++ ) { { ht
[ i
] . weight
= HT
[ i
] . weight
; ht
[ i
] . index
= HT
[ i
] . index
; } } int temp_index
= 0 ; int temp_weight
= 0 ; for ( int i
= 1 ; i
< length
; i
++ ) { for ( int j
= 1 ; j
<= length
- i
; j
++ ) { if ( ht
[ j
] . weight
> ht
[ j
+ 1 ] . weight
) { temp_weight
= ht
[ j
] . weight
; ht
[ j
] . weight
= ht
[ j
+ 1 ] . weight
; ht
[ j
+ 1 ] . weight
= temp_weight
; temp_index
= ht
[ j
] . index
; ht
[ j
] . index
= ht
[ j
+ 1 ] . index
; ht
[ j
+ 1 ] . index
= temp_index
; } } } for ( int i
= 1 ; i
<= length
; i
++ ) { if ( HT
[ ht
[ i
] . index
] . parent
== 0 ) { s1
= ht
[ i
] . index
; HT
[ ht
[ i
] . index
] . parent
= length
+ 1 ; break ; } } for ( int j
= 1 ; j
<= length
; j
++ ) { if ( HT
[ ht
[ j
] . index
] . parent
== 0 ) { s2
= ht
[ j
] . index
; HT
[ ht
[ j
] . index
] . parent
= length
+ 1 ; break ; } } }
void createHuffmanTree ( HuffmanTree
& HT
, int n
, char * alphabet
)
{
if ( n
< 1 ) return ; int m
= 2 * n
- 1 ; int number
= 0 ; HT
= new HTNode
[ m
+ 1 ] ; for ( int i
= 1 ; i
<= m
; i
++ ) { HT
[ i
] . info
= alphabet2
[ i
- 1 ] ; HT
[ i
] . parent
= 0 ; HT
[ i
] . lchild
= 0 ; HT
[ i
] . rchild
= 0 ; HT
[ i
] . index
= i
; } for ( int j
= 0 ; j
< 26 ; j
++ ) { if ( alphabet
[ j
] > 0 ) HT
[ ++ number
] . weight
= alphabet
[ j
] ; }
int a
, b
; for ( int i
= n
+ 1 ; i
<= m
; i
++ ) { select ( HT
, i
- 1 , a
, b
) ; HT
[ a
] . parent
= i
; HT
[ b
] . parent
= i
; HT
[ i
] . lchild
= a
; HT
[ i
] . rchild
= b
; HT
[ i
] . weight
= HT
[ a
] . weight
+ HT
[ b
] . weight
; } for ( int a
= 1 ; a
<= 2 * n
- 1 ; a
++ ) { printf ( "下標(biāo)為%d的權(quán)為 %d 父親節(jié)點(diǎn):%d,左孩子:%d,右孩子:%d\n" , HT
[ a
] . index
, HT
[ a
] . weight
, HT
[ a
] . parent
, HT
[ a
] . lchild
, HT
[ a
] . rchild
) ; } }
void CreatHuffmanCode ( HuffmanTree HT
, HuffmanCode
& HC
, int n
)
{ HC
= new
char * [ n
+ 1 ] ; char * cd
; cd
= new
char [ n
] ; cd
[ n
- 1 ] = '\0' ; int start
; int c
; int f
; for ( int i
= 1 ; i
<= n
; i
++ ) { start
= n
- 1 ; c
= i
, f
= HT
[ i
] . parent
; while ( f
!= 0 ) { -- start
; if ( HT
[ f
] . lchild
== c
) cd
[ start
] = '0' ; else cd
[ start
] = '1' ; c
= f
, f
= HT
[ f
] . parent
; } HC
[ i
] = new
char [ n
- start
] ; strcpy ( HC
[ i
] , & cd
[ start
] ) ; } delete cd
; for ( int i
= 1 ; i
<= n
; i
++ ) printf ( "%c:%s " , alphabet2
[ i
- 1 ] , HC
[ i
] ) ; printf ( "\n" ) ;
}
void encode ( SqString
& S
, HuffmanCode HC
, int n
, HuffmanTree HT
, char * sum
)
{ for ( int i
= 0 ; S
. String
[ i
] != '\0' ; i
++ ) { for ( int j
= 1 ; j
<= n
; j
++ ) { if ( S
. String
[ i
] == HT
[ j
] . info
) { strcat ( sum
, HC
[ j
] ) ; strcpy ( HT
[ j
] . code
, HC
[ j
] ) ; break ; } } } printf ( "%s\n" , sum
) ;
}
void decode ( char * sum
, int n
, HuffmanTree HT
)
{ int index
= 2 * n
- 1 ; int j
= 0 ; while ( sum
[ j
] != '\0' ) { if ( sum
[ j
] == '0' ) index
= HT
[ index
] . lchild
; else index
= HT
[ index
] . rchild
; if ( HT
[ index
] . lchild
== 0 && HT
[ index
] . rchild
== 0 ) { printf ( "%c" , HT
[ index
] . info
) ; index
= 2 * n
- 1 ; } j
++ ; } } int main ( )
{ SqString S
; do { gets ( S
. String
) ; HuffmanTree HT
; char sum
[ 100 ] = { "" } ; int num
= 0 ; char alphabet
[ 26 ] = { 0 } ; char_statiscal ( S
, alphabet
, num
, alphabet2
) ; createHuffmanTree ( HT
, num
, alphabet
) ; HuffmanCode HC
; CreatHuffmanCode ( HT
, HC
, num
) ; encode ( S
, HC
, num
, HT
, sum
) ; decode ( sum
, num
, HT
) ; printf ( "\n" ) ; } while ( S
. String
!= "00" ) ; }
關(guān)于n個(gè)字符的哈弗曼編碼的計(jì)算時(shí)間復(fù)雜度為(nlogn) 因?yàn)樽钚《训牟迦牒蛣h除都需要(logn)的時(shí)間,而n-1次的合并就需要n(logn)
總結(jié)
以上是生活随笔 為你收集整理的数据结构——哈弗曼编码问题 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。