[DSA] Explicação detalhada da árvore Tree-Huffman (3)

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.
    Insira a descrição da imagem aqui
    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

Insira a descrição da imagem aqui
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

Insira a descrição da imagem aqui
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

Insira a descrição da imagem aqui

Alfabeto UMA D FCBH POR EXEMPLO
Frequência 80 75 118 95

Insira a descrição da imagem aqui

Alfabeto DE ANÚNCIOS FCBH POR EXEMPLO
Frequência 155 118 95

Insira a descrição da imagem aqui

Alfabeto DE ANÚNCIOS FCBHEG
Frequência 155 213

Insira a descrição da imagem aqui
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
Insira a descrição da imagem aqui
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
    !Insira a descrição da imagem aqui

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.
Publicado 134 artigos originais · Gostei 119 · Visite mais de 310.000

Acho que você gosta

Origin blog.csdn.net/jobbofhe/article/details/102502565
Recomendado
Clasificación