データ構造とアルゴリズム-HuffmanツリーとHuffman符号化
14514 ワード
ハフマンツリーは特殊な構造の二叉木であり、ハフマンツリーによって設計されたバイナリプレフィックス符号化であり、ハフマン符号化とも呼ばれ、通信分野で広く応用されている.word 2 vecモデルでは,階層Softmaxを構築する過程で,Huffmanツリーの知識も用いた.
通信では、送信された文字をバイナリ文字列に変換する必要があり、送信されたメッセージが「AFTERDATAEARAREAREARTAREA」であると仮定し、現在はそのメッセージを符号化する必要がある.
ツリーには、以下に示すツリーについて、いくつかの基本的な概念があります.パス パスとは、ある木において、あるノードから別のノードへの分岐からなるパスであり、ノード8からノード1へのパスを下図に示す.経路長 パス長はパス上のブランチの数を指し、上図ではパス長は2である.ノードの重み ノードの重みとは、図中の各ノードの値のように、ツリー内の各ノードに与えられた非負の値を指す.ノードの重み付き経路長 ノードの重み付きパス長とは、ルートノードからノードまでのパス長とノードの重みの積を指します.たとえば、1ノードの重み付きパス長は:2です.ツリーの重み付きパス長 ツリーの重み付きパスの長さは、すべてのリーフノードの重み付きパスの長さの和を指します.
このような概念があり、Huffmanツリーについては、次のように定義されています.
nの重み値をn個の葉ノードとして与え、1本の二叉木を構築し、この二叉木の重み付き経路長が最小に達すると、このような二叉木を最適二叉木と呼び、Huffman木とも呼ばれる.
以上の定義から分かるように,Huffmanツリーは重み付き経路長が最も小さい二叉木であり,上の二叉木に対してその構造が完成したHuffmanツリーは次のようになる.
上記のHuffmanツリーから分かるように,ノードの重みが小さいほど,ツリーのルートノードから遠くなる.では、ハフマンの木をどのように構築すればいいのでしょうか.上記のメッセージを例にとると、まず、各文字の出現回数をノードとして統計する必要がある.
次に、Huffmanツリーを構築します.次の手順を繰り返します. 各ノードを重み値でソート:D-F-T-E-R-A 重み値が最も小さい2つのノードを選択し、ここではDとFのために新しいノードを生成し、ノードの重みはこの2つのノードの重みの和であり、2 である.
最後のルートノード のみが残るまで
上記の手順に従って、このメッセージのHuffmanツリーの生成プロセスは次のとおりです.
ツリー内のノードの構造は次のとおりです.
Huffmanツリーの構築手順は次のとおりです.
ここで、map構造のwordは各文字が現れる頻度であり、ファイルから解析され、解析されたコードは:
Huffmanツリーを構築した後、Huffmanツリーを巡回するには、先行巡回と中間巡回を使用します.先行巡回のコードは次のとおりです.
中間パスのコードは次のとおりです.
得られた構造は上図の構造と一致した.
上記のHuffmanツリーの構成により、Huffmanツリーを用いて各文字を符号化する必要がある.この符号化はHuffman符号化とも呼ばれ、Huffman符号化はプレフィックス符号化であり、1つの文字の符号化は別の文字符号化のプレフィックスではない.ここで約束します.重み値の小さい最も左のノード、重み値の大きいものを右のノード とする.左子符号は0、右子符号は1 従って、上記の符号化形態は、下図のようになる.
上図から、Eノードの符号化は:00、同理、Dノードの符号化は1001
Huffman符号化の実現過程は以下の通りである.
上記のコードを使用して、テストの主な関数は次のとおりです.
print_leaf関数は、各リーフノードのHuffman符号化を印刷するために使用され、具体的には、以下のように実現される.
destory_huffman_tree関数は、次のように具体的に実装されるHuffmanツリーを破棄するために使用されます.
最終的な結果は次のとおりです.
「大話データ構造」 「データ構造」(C言語版)
通信では、送信された文字をバイナリ文字列に変換する必要があり、送信されたメッセージが「AFTERDATAEARAREAREARTAREA」であると仮定し、現在はそのメッセージを符号化する必要がある.
一、ハフマンの木の基本概念
ツリーには、以下に示すツリーについて、いくつかの基本的な概念があります.
このような概念があり、Huffmanツリーについては、次のように定義されています.
nの重み値をn個の葉ノードとして与え、1本の二叉木を構築し、この二叉木の重み付き経路長が最小に達すると、このような二叉木を最適二叉木と呼び、Huffman木とも呼ばれる.
以上の定義から分かるように,Huffmanツリーは重み付き経路長が最も小さい二叉木であり,上の二叉木に対してその構造が完成したHuffmanツリーは次のようになる.
二、ハフマンツリーの構築
上記のHuffmanツリーから分かるように,ノードの重みが小さいほど,ツリーのルートノードから遠くなる.では、ハフマンの木をどのように構築すればいいのでしょうか.上記のメッセージを例にとると、まず、各文字の出現回数をノードとして統計する必要がある.
次に、Huffmanツリーを構築します.
上記の手順に従って、このメッセージのHuffmanツリーの生成プロセスは次のとおりです.
ツリー内のノードの構造は次のとおりです.
#define LEN 512
struct huffman_node{
char c;
int weight;
char huffman_code[LEN];
huffman_node * left;
huffman_node * right;
};
Huffmanツリーの構築手順は次のとおりです.
int huffman_tree_create(huffman_node *&root, map<char, int> &word){
char line[MAX_LINE];
vector<huffman_node *> huffman_tree_node;
map<char, int>::iterator it_t;
for (it_t = word.begin(); it_t != word.end(); it_t++){
//
huffman_node *node = (huffman_node *)malloc(sizeof(huffman_node));
node->c = it_t->first;
node->weight = it_t->second;
node->left = NULL;
node->right = NULL;
huffman_tree_node.push_back(node);
}
// Huffman
while (huffman_tree_node.size() > 0){
// weight
sort(huffman_tree_node.begin(), huffman_tree_node.end(), sort_by_weight);
//
if (huffman_tree_node.size() == 1){//
root = huffman_tree_node[0];
huffman_tree_node.erase(huffman_tree_node.begin());
}else{
//
huffman_node *node_1 = huffman_tree_node[0];
huffman_node *node_2 = huffman_tree_node[1];
//
huffman_tree_node.erase(huffman_tree_node.begin());
huffman_tree_node.erase(huffman_tree_node.begin());
//
huffman_node *node = (huffman_node *)malloc(sizeof(huffman_node));
node->weight = node_1->weight + node_2->weight;
(node_1->weight < node_2->weight)?(node->left=node_1,node->right=node_2):(node->left=node_2,node->right=node_1);
huffman_tree_node.push_back(node);
}
}
return 0;
}
ここで、map構造のwordは各文字が現れる頻度であり、ファイルから解析され、解析されたコードは:
int read_file(FILE *fn, map<char, int> &word){
if (fn == NULL) return 1;
char line[MAX_LINE];
while (fgets(line, 1024, fn)){
fprintf(stderr, "%s
", line);
// ,
char *p = line;
while (*p != '\0' && *p != '
'){
map<char, int>::iterator it = word.find(*p);
if (it == word.end()){// ,
word.insert(make_pair(*p, 1));
}else{
it->second ++;
}
p ++;
}
}
return 0;
}
Huffmanツリーを構築した後、Huffmanツリーを巡回するには、先行巡回と中間巡回を使用します.先行巡回のコードは次のとおりです.
void print_huffman_pre(huffman_node *node){
if (node != NULL){
fprintf(stderr, "%c\t%d
", node->c, node->weight);
print_huffman_pre(node->left);
print_huffman_pre(node->right);
}
}
中間パスのコードは次のとおりです.
void print_huffman_in(huffman_node *node){
if (node != NULL){
print_huffman_in(node->left);
fprintf(stderr, "%c\t%d
", node->c, node->weight);
print_huffman_in(node->right);
}
}
得られた構造は上図の構造と一致した.
三、ハフマンツリーからハフマン符号化を生成する
上記のHuffmanツリーの構成により、Huffmanツリーを用いて各文字を符号化する必要がある.この符号化はHuffman符号化とも呼ばれ、Huffman符号化はプレフィックス符号化であり、1つの文字の符号化は別の文字符号化のプレフィックスではない.ここで約束します.
上図から、Eノードの符号化は:00、同理、Dノードの符号化は1001
Huffman符号化の実現過程は以下の通りである.
int get_huffman_code(huffman_node *&node){
if (node == NULL) return 1;
// ,
huffman_node *p = node;
queue<huffman_node *> q;
q.push(p);
while(q.size() > 0){
p = q.front();
q.pop();
if (p->left != NULL){
q.push(p->left);
strcpy((p->left)->huffman_code, p->huffman_code);
char *ptr = (p->left)->huffman_code;
while (*ptr != '\0'){
ptr ++;
}
*ptr = '0';
}
if (p->right != NULL){
q.push(p->right);
strcpy((p->right)->huffman_code, p->huffman_code);
char *ptr = (p->right)->huffman_code;
while (*ptr != '\0'){
ptr ++;
}
*ptr = '1';
}
}
return 0;
}
上記のコードを使用して、テストの主な関数は次のとおりです.
int main(){
//
FILE *fn = fopen("huffman", "r");
huffman_node *root = NULL;
map<char, int> word;
read_file(fn, word);
huffman_tree_create(root, word);
fclose(fn);
fprintf(stderr, "pre-order:
");
print_huffman_pre(root);
fprintf(stderr, "in-order:
");
print_huffman_in(root);
get_huffman_code(root);
fprintf(stderr, "the final result:
");
print_leaf(root);
destory_huffman_tree(root);
return 0;
}
print_leaf関数は、各リーフノードのHuffman符号化を印刷するために使用され、具体的には、以下のように実現される.
void print_leaf(huffman_node *node){
if (node != NULL){
print_leaf(node->left);
if (node->left == NULL && node->right == NULL) fprintf(stderr, "%c\t%s
", node->c, node->huffman_code);
print_leaf(node->right);
}
}
destory_huffman_tree関数は、次のように具体的に実装されるHuffmanツリーを破棄するために使用されます.
void destory_huffman_tree(huffman_node *node){
if (node != NULL){
destory_huffman_tree(node->left);
destory_huffman_tree(node->right);
free(node);
node = NULL;
}
}
最終的な結果は次のとおりです.