前面几篇文章已经把encoder彻底弄懂了,现在到了最后一步,编写decoder。
当时做题的时候写的代码在这里。是老老实实定义了二叉树结点、用指针实现的大学生标准写法。调试的痕迹也都还在,欢迎大家下载嘲笑~
哈夫曼编码大家肯定都学过,自己写一份也不难(确信)。现在写题解我想使用一个无需指针实现二叉树的方法:-)
构造哈夫曼树
在程序开头我们必须构造哈夫曼树,我直接贴代码
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include "main.h"
uint8_t tree[256 * 2 - 1];
size_t left[256 * 2 - 1];
size_t right[256 * 2 - 1];
void init()
{
int count = 0;
for (int i = 0; i < 256; i++)
{
uint32_t row = ((uint32_t *)table)[i];
uint32_t val = row & 0x00ffffff;
uint32_t len = row >> 24;
size_t ptr = 0;
for (int j = 0; j < len; j++)
{
size_t &target = (val & (1 << j)) ? left[ptr] : right[ptr];
if (!target)
target = ++count;
ptr = target;
}
tree[ptr] = i;
assert(!left[ptr] && !right[ptr]);
}
}
使用了一个断言,可以确认应该是叶子的结点没有子节点。断言未触发说明题目给的确实是一个无前缀编码。
Decode it!
int main()
{
init();
size_t ptr = 0;
uint32_t v1, r12 = 8;
while (1)
{
// read bit
if (r12 == 8)
{
if ((v1 = getchar()) == EOF)
return 0;
r12 = 0;
}
int c = (v1 & (1 << r12++)) != 0;
// decode
size_t target = c ? left[ptr] : right[ptr];
if (target)
ptr = target;
else
{
putchar(tree[ptr]);
ptr = c ? left[0] : right[0];
}
}
}
这样就行了……让我们试试:
$ g++ -o decoder decoder.cpp
$ cat output.bin | ./decoder
nactf{4ss3mbly_huffm4n_c0d1ng_1s_fun_6JkDGFG3qTAYVe8h}
b$
???? fun? fun你个大头鬼?