O que é uma árvore Huffman
- A definição da Enciclopédia Baidu
fornece N pesos como nós de folha N. para construir uma árvore binária.Se o comprimento do caminho ponderado atingir o mínimo, essa árvore binária será chamada de árvore binária ideal, também conhecida como árvore de Huffman ( Árvore de Huffman). A árvore Huffman é a árvore com o menor comprimento do caminho ponderado e os nós com pesos maiores estão mais próximos da raiz.
Árvore Huffman
A definição acima é muito acadêmica e uma expressão muito rigorosa, mas sempre parece não ser tão fácil de entender. Aqui, não vamos falar sobre teoria, vamos examinar diretamente como uma árvore de Huffman é construída e entender conceitos abstratos através de coisas figurativas.
Como construir uma árvore Huffman
Abaixo, usamos um exemplo de codificação de Huffman para entender a árvore de Huffman.
- Problema de fundo
Aqui, ilustramos com um exemplo.
Agora, um artigo em inglês precisa ser enviado de A a B. O requisito é que o comprimento do código seja o menor. Existem 26 letras em inglês no total.Se a capitalização for diferente, será 52. Então precisamos de 6 bits para codificar (2 ^ 5 <52 <2 ^ 6 = 64). Se este artigo tiver 10.000 letras, o comprimento da codificação será 10000 * 6. Sabemos que no artigo a frequência de ocorrência de cada letra é diferente, pensando: use comprimentos diferentes de dígitos de codificação para letras de frequências diferentes, ou seja, o menor comprimento de codificação das letras mais frequentes, as letras mais frequentes Comprimento de codificação longo. Isso minimizará o comprimento total do artigo inteiro. Melhore a eficiência da transmissão.
A implementação é a seguinte:
tabela de frequência de caracteres
Alfabeto | UMA | B | C | D | E | F | G | H |
---|---|---|---|---|---|---|---|---|
Frequência | 80 | 30 | 20 | 75 | 40. | 8 | 55 | 60 |
De acordo com a tabela acima, sabemos que a codificação de A é a mais curta e a codificação de F é a mais longa. A frequência aqui é arbitrariamente especificada por mim e a probabilidade da letra real aparecer é contada na criptografia (você pode consultar a frequência da letra ).
- Construção
Etapa 1: Selecione as duas letras F e C com a menor frequência e use-as para formar uma árvore binária: a menor é a criança esquerda e a maior é a criança certa. E pegue a soma das frequências de F e C como o nó raiz e retorne-o à tabela de frequências.
Etapa 2: repita as operações acima continuamente.
F + C é menor que B, então 28 está na posição da criança esquerda.
Alfabeto | UMA | B | D | E | FC | G | H |
---|---|---|---|---|---|---|---|
Frequência | 80 | 30 | 75 | 40. | 28. | 55 | 60 |
Verificou-se que o menor neste momento é 40 e 55 (E e G)
Alfabeto | UMA | D | E | FCB | G | H |
---|---|---|---|---|---|---|
Frequência | 80 | 75 | 40. | 58. | 55 | 60 |
Os menores neste momento são 58 e 60 (EG e H)
Alfabeto | UMA | D | FCB | POR EXEMPLO | H |
---|---|---|---|---|---|
Frequência | 80 | 75 | 58. | 95 | 60 |
Alfabeto | UMA | D | FCBH | POR EXEMPLO |
---|---|---|---|---|
Frequência | 80 | 75 | 118 | 95 |
Alfabeto | DE ANÚNCIOS | FCBH | POR EXEMPLO |
---|---|---|---|
Frequência | 155 | 118 | 95 |
Alfabeto | DE ANÚNCIOS | FCBHEG |
---|---|---|
Frequência | 155 | 213 |
Neste ponto, a construção da árvore Huffman está concluída. Como a codificação mencionada acima foi implementada? De acordo com o binário, o lado esquerdo é marcado com 0 e o lado direito é marcado com 1. A sequência de 0s e 1s ao longo da direção da árvore até o nó da folha onde a letra está localizada é o código Huffman da letra. Como segue: A
tabela a seguir é o código final de todas as letras
Alfabeto | Código |
---|---|
UMA | 01 |
B | 1101 |
C | 11001 |
D | 00 |
E | 100 |
F | 11000 |
G | 101 |
H | 111 |
Se eu quiser enviar quatro letras do ABC, o código é 0 111111 1111101, um total de 14 bits. Se você codificar de acordo com as 6 primeiras letras, o comprimento será 18.
Implementação do código em árvore Huffman
Ao construir uma árvore Huffman, você precisa filtrar os dois nós com o menor valor de cada vez, de acordo com o valor de peso de cada nó e, em seguida, criar uma árvore binária.
A idéia de encontrar os dois nós com o menor valor de peso é: começando no início do grupo de árvores, encontre primeiro os dois nós sem nós pais (indicando que eles não foram usados para construir uma árvore) e, em seguida, faça o acompanhamento sem nós pais Compare os nós, por sua vez, há dois casos a serem considerados:
- Se for menor que o menor dos dois nós, mantenha esse nó e exclua o nó maior original;
- Se estiver entre os valores de peso dos dois nós, substitua o nó maior original;
Estrutura de dados de estrutura de árvore de Huffman
// 哈夫曼树结点结构
typedef int Type;
typedef struct HuffmanNode_
{
Type weight; // 节点权重
Type parent, left, right; //父结点、左孩子、右孩子在数组中的位置下标
}Node, *HuffmanTree;
// 选中频率最小的两个数据
// HT数组中存放的哈夫曼树,end表示HT数组中存放结点的最终位置,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的位置
void select(HuffmanTree HT, int *pos1, int *pos2, int end)
{
int min1 = 0, min2 = 0;
int i = 1; // 数组的 0 号元素作为根节点的位置所以不使用
// 找到没有构建成树的第一个节点
while (HT[i].parent != 0 && i <= end)
{
i++;
}
min1 = HT[i].weight;
*pos1 = i;
i++;
// 找到没有构建成树的第二个节点
while(HT[i].parent != 0 && i <= end)
{
i++;
}
min2 = HT[i].weight;
if (min2 < min1)
{
min2 = min1;
*pos2 = *pos1;
min1 = HT[i].weight;
*pos1 = i;
}
else
{
*pos2 = i;
}
// 取得两个节点之后,跟之后所有没有构建成树的节点逐一比较,最终获取最小的两个节点
for (int j = i+1; j <= end; ++j)
{
// 如果已经存在父节点,也就是已经被构建树了,则跳过
if (HT[j].parent != 0)
{
continue;
}
// 如果比min1 还小,将min2 = 敏, min1修改为新的节点下标
if (HT[j].weight < min1)
{
min2 = min1;
min1 = HT[j].weight;
*pos2 = *pos1;
*pos1 = j;
}
else if (HT[j].weight < min2 && HT[j].weight > min1)
{
// 如果大于 min1 小于 min2
min2 = HT[j].weight;
*pos2 = j;
}
}
}
// 创建完整的哈夫曼树
// HT为地址传递的存储哈夫曼树的数组,w为存储结点权重值的数组,n为结点个数
HuffmanTree init_huffman_tree(Type *weight, int node_num)
{
if (node_num <= 1)
{
// 只有一个节点那么编码就是 0
return NULL;
}
int tree_node_num = node_num * 2 - 1; // 根节点不使用
HuffmanTree p = (HuffmanTree)malloc((tree_node_num+1) * sizeof(Node));
// 初始化哈夫曼数组中的所有节点
for (int i = 1; i <= tree_node_num; ++i)
{
if (i <= node_num)
{
(p+i)->weight = *(weight+i-1); // 第0个位置不使用
}
else
{
(p+i)->weight = 0;
}
(p+i)->parent = 0;
(p+i)->left = 0;
(p+i)->right = 0;
}
return p;
}
void close_huffman_tree(HuffmanTree HT)
{
if (HT)
{
free(HT);
HT = NULL;
}
}
void create_huffman_tree(HuffmanTree HT, int node_num)
{
if (NULL == HT || node_num <= 1)
{
return;
}
int tree_node_num = node_num * 2 - 1; // 根节点不使用
for (int i = node_num + 1; i <= tree_node_num; ++i)
{
int pos1 = -1, pos2 = -1;
// 找到频率最小的连个节点
select(HT, &pos1, &pos2, i-1);
printf("当前最小的两个节点 [%d %d]\n", HT[pos1].weight, HT[pos2].weight);
// 这里使用下表来表示父子关系
HT[pos1].parent = HT[pos2].parent = i; // pos1 位置的元素和pos2位置的元素 的父节点就是,第 i个位置的元素
HT[i].left = pos1; // 父节点的左后孩子赋值
HT[i].right = pos2;
HT[i].weight = HT[pos1].weight + HT[pos2].weight; // 父节点的权重等于 左右孩子权重的和
}
}
- Código de teste
void print(HuffmanTree HT, int node_num)
{
if (NULL == HT)
{
printf("数组为空\n");
return;
}
int tree_node_num;
for (int i = 1; i < tree_node_num; ++i)
{
printf("%d 的父节点:%d 左孩子:%d 右孩子:%d\n", HT[i].weight, HT[HT[i].parent].weight, HT[i].left, HT[i].right);
}
}
int main(int argc, char const *argv[])
{
Type weight[8] = {80, 30, 20, 75, 40, 8, 55, 60};
int node_num = sizeof(weight) / sizeof(Type);
HuffmanTree HT = init_huffman_tree(weight, node_num);
create_huffman_tree(HT, node_num);
print(HT, node_num);
close_huffman_tree(HT);
return 0;
}
- Resultados do teste
!
Por que projetar uma árvore Huffman
- A árvore Huffman é usada principalmente para a codificação Huffman, cuja principal função é usar atributos de frequência para codificação e, finalmente, atingir o objetivo: permitir que dados de alta frequência tenham codificação curta e dados de baixa frequência tenham codificação longa.
- A codificação Huffman não é adequada para todos os cenários; é mais adequada para codificação de dados com várias alterações de frequência.