ハフマン木 - コーデック
はじめに:
ハフマン木は、その後、入力が表示された文字の文字列の数に応じた重み値に指定された文字列コードの重量の大きさに応じて、符号化又は復号化の文字列を設定することができます、これは、圧縮または伸張されたデータで使用され、コーデックの文字することができます。
しかし、ハフマン木の利点はどこ?
1、それは大量の文字(大文字のすなわち重量)を有していることであるエンコーディングは、より多くの数、データの圧縮を確実にする短いコーディングを表示されます表示されるよりも、文字エンコーディングの少し短いが、表示されます。
2、コンパイルされたコードは、Cコードは00であるが、互いを覆う、つまり、曖昧ではありません、そのようなコードは00100であり、Bコードは、001である保証はありません,,この場合、それは00100のために可能です、それができるBCで、ハフマン符号化ツリーは、この問題に直面することはありません。
どのように達成するために
ハフマン木コーデックがデータの三種類を達成するために必要、プライオリティキュー、ツリー内のノードを保存するために使用され、第二のツリーは、復号のために、第三のコード表コードとして使用されるテーブルです。私たちは、最初の3つのデータ構造について一つ一つを紹介してみましょう:
1、プライオリティキュー
プライオリティキューは、ツリーノード文字に格納された加重値に応じて優先順位を決定するために、ツリーのノードに格納され、重量、放電の小さな優先順位、より前方に位置されています。すなわち、第1ノードの優先順位記憶された最小値、最小量です。
データの種類
//优先级队列,struct TNode表示树的结点,在后面介绍
typedef struct QNode
{
struct TNode* val; //树的结点,其实也就是数据域
int priority; //优先级
struct QNode* next; //指针域
}*Node;
typedef struct Queue
{
int size; //队列大小
struct QNode* front; //队列头指针
}queue;
2、木
ツリーは、文字の内部に格納さだけでなく、ポインタの彼の左と右の子ノードを指しています。たとえば、次の図、図は、書店で文字の優先順位に見えますが、実際には、追加し、より複雑に感じるので、私はかかったが、利便性を理解するために、私は、地図上にマークされていないことができますが。
データの種類
//树
typedef struct TNode
{
char data; //字符值
struct TNode* left; //左孩子
struct TNode* right; //右孩子
}*Tree;
3、表
このテーブルは、符号化された文字と表示するときに使用される文字エンコーディングの内部に格納され、実際にコード表です。
データの種類
//表
typedef struct BNode
{
char code[256]; //编码
char symbol; //字符
struct BNode* next; //指向下一个
}*bNode;
typedef struct Table
{
struct BNode* first; //表头
struct BNode* last; //表尾
}*table;
考え
彼は第1のユーザ入力ではなく、発生の統計の周波数の重みを設定したときに、我々はまた、動作するため、簡単にするために、私たちは、教えてユーザー入力、私はコードの発生頻度に基づいて統計を掲載前回の記事であることを起こる、興味を持っている見ることができます。それはデータ型の多くは、半分は少しめまいを感じる書くことができる。使用しているので、あなたが開始する前に、その者の散髪考えを聞かせて:
以前は、Bを設定し、3つのデータcは、その重みは6,1,2ました
1、首先要根据用户输入的每个字符的权值,创建出一个一个的树结点,然后将其按照优先级的大小存入优先级队列中,按从小到大的顺序,具体实现我会在后面贴。
2、根据优先级队列中存放的树的结点构建起一棵树。
先出队前两个结点,然后创建一个新的树的结点,新的树的结点的权值就等于出队的两个结点的权值之和,但其没有字符域,也就是说它不是一个真正的树的结点,我们称其为假树结点,对应称为真树结点。
让出队的两个真树结点作为新得到的假树结点的左右孩子,优先级小的真树结点(也就是先出队的真树结点)作为左孩子,另一个为右孩子。
出队后
b和c为真树结点,最上面权值为3的为假树结点
最后将新创建的假树结点又入队,继续循环操作,直到队列只剩一个结点,那个结点就是假树结点,最后也要作为Huffman树的根节点root。
新的假树结点入队后
到最后就是下面这样
队列只剩最后一个假树结点,而且作为所构建Huffman树的根节点root
3、遍历整棵树建起一张码表,通过观察我们发现,真正有意义的真树结点其实都是叶子节点,所以我们在遍历的时候将所有的叶子节点的编码和字符存入表中即可。
我们规定遍历树建立表的时候,往左孩子访问一层给码值加0,往右就加1。比如刚刚介绍树的时候贴的那张图,b是00,c是01,a是1。
下面是建立起来的码表
构建Huffman树和创建编码表的实现过程
看完思路之后再看实现过程,我们先看创建队列时候的一系列操作:
因为为了方便我用了部分C++语法,所以分配内存会是用new,释放内存就是delete,就和C语言里malloc和free是一个作用,其他的都一样。
队列的初始化:
queue Init_queue()
{
queue q;
q.size = 0;
q.front = new struct QNode;
if (!q.front)
{
printf("分配失败!\n");
exit(1);
}
q.front->next = NULL;
return q;
}
队列的插入:
//插入,根据优先级
bool EnQueue(queue& q, Tree avl, int weight)
{
Node newp = new struct QNode;
newp->val = avl;
newp->priority = weight;
if (q.size == 0 || q.front == NULL) //空表
{
newp->next = NULL;
q.front = newp;
q.size = 1;
return true;
}
else //中间位置,需要迭代
{
if (weight <= q.front->priority) //比第一个都小
{
newp->next = q.front;
q.front = newp;
q.size++;
return true;
}
else //中间位置
{
Node beforp = q.front;
while (beforp->next != NULL)
{
if (weight <= beforp->next->priority)
{
newp->next = beforp->next;
beforp->next = newp;
q.size++;
return true;
}
else
{
beforp = beforp->next;
}
}
//需要插在队列最后
if (beforp->next == NULL)
{
newp->next = NULL;
beforp->next = newp;
q.size++;
return true;
}
}
}
return true;
}
创建一个队列:
需要用户输入每个字符和对应的优先级
//创建队列
queue Create_Queue()
{
queue q = Init_queue();
while (1)
{
char symbol;
int weight;
cin >> symbol >> weight; //C++里的输入,输入symnol和weight
if (weight == 0) //如果输入的权值为0,表示输入结束
break;
Tree t = new struct TNode;
t->data = symbol;
t->left = NULL;
t->right = NULL;
EnQueue(q, t, weight);
}
return q;
}
最小プライオリティキューポップノード:
//弹出队列优先级最小的
Tree Dequeue(queue& q)
{
if (q.front == NULL)
{
cout << "空队!" << endl;
exit(1);
}
Node p = q.front;
q.front = p->next;
Tree e = p->val;
q.size--;
delete[] p;
return e;
}
機能ツリーは、ツリーはプライオリティキューに基づいて作成されます。
//树的函数
//创建一棵树
Tree Create_Tree(queue& q)
{
while (q.size != 1)
{
int priority = q.front->priority + q.front->next->priority;
Tree left = Dequeue(q);
Tree right = Dequeue(q);
Tree newTNode = new struct TNode;
newTNode->left = left;
newTNode->right = right;
EnQueue(q, newTNode, priority);
}
Tree root = new struct TNode;
root = Dequeue(q);
return root;
}
関数テーブル、ツリーからテーブルを作成します。
//创建一张表
table Create_Table(Tree root)
{
table t = new struct Table;
t->first = NULL;
t->last = NULL;
char code[256];
int k = 0;
travel(root, t, code, k);
return t;
}
旅行機能実現するための関数テーブル:
トラベル機能は、補間フッターを使用して、テーブルを確立するために、ツリーのトラバースを表しています
void travel(Tree root, table& t, char code[256], int k)
{
if (root->left == NULL && root->right == NULL)
{
code[k] = '\0';
bNode b = new struct BNode;
b->symbol = root->data;
strcpy(b->code, code);
b->next = NULL;
//尾部插入法
if (t->first == NULL) //空表
{
t->first = b;
t->last = b;
}
else
{
t->last->next = b;
t->last = b;
}
}
if (root->left != NULL)
{
code[k] = '0';
travel(root->left, t, code, k + 1);
}
if (root->right != NULL)
{
code[k] = '1';
travel(root->right, t, code, k + 1);
}
}
コーデック
このように、ハフマン符号化テーブル、およびツリーは現在、ハフマンツリーの初期テストの機能を実現するコーデック、建物を終えました。
コード:
エンコーディングのためのコーディングテーブルを渡す必要
void EnCode(table t, char* str)
{
cout << "EnCodeing............./" << endl;
int len = strlen(str);
for (int i = 0; i < len; i++)
{
bNode p = t->first;
while (p != NULL)
{
if (p->symbol == str[i])
{
cout << p->code;
break;
}
p = p->next;
}
}
cout << endl;
}
デコーディング:
エンコードにハフマン木を渡す必要
void DeCode(Tree root, char* str)
{
cout << "DeCode............./" << endl;
Tree p = root;
int len = strlen(str);
for (int i = 0; i < len; i++)
{
if (p->left == NULL && p->right == NULL)
{
cout << p->data;
p = root;
}
if (str[i] == '0')
p = p->left;
if (str[i] == '1')
p = p->right;
if (str[i] != '0' && str[i] != '1')
{
cout << "The Input String Is Not Encoded correctly !" << endl;
return;
}
}
if (p->left == NULL && p->right == NULL)
cout << p->data;
cout << endl;
}
テストデータ
int main()
{
queue q = Create_Queue();
Tree root = Create_Tree(q);
table t = Create_Table(root);
char str[256];
cout << "请输入要编码的字符:" << endl;
cin >> str;
EnCode(t, str);
cout << "请输入要解码的码值:" << endl;
char str1[256];
cin >> str1;
DeCode(root, str1);
}
スクリーンショットを添付: