Dando continuidade à elaboração do Algoritmo de Huffman, vamos desenvolver nesta aula o passo 3: construir a Árvore de Huffman a partir da lista ordenada elaborada no passo 2 da aula anterior.
Agora que já temos nossa lista ordenada pronta, precisamos de uma repetição que irá remover os dois primeiros nós da lista, juntar estes nós como filhos de um terceiro nó e inseri-lo novamente na lista mantendo a ordenação pela frequência. Esta repetição deve executar até que reste apenas um nó na lista. Este nó é a raiz da árvore de huffmam como apresentado na figura a seguir.
Na aula anterior nós criamos a lista encadeada, contudo, não fizemos uma função de remoção. Para montar a árvore de huffman esta função é necessária. Então, vamos iniciar por ela. Esta função recebe o ponteiro para a lista, verifica se o início da lista é diferente de null, cria um ponteiro auxiliar para o início da lista e altera o início da lista para o próximo nó. Por fim retorna o ponteiro para o primeiro nó removido. É esta lógica que é implementada no trecho de código a seguir.
/* Função para remover um nó da lista encadeada */ No* remove_no_inicio(Lista *lista){ No *aux = NULL; if(lista->inicio){ aux = lista->inicio; lista->inicio = aux->proximo; aux->proximo = NULL; lista->tam--; } return aux; }
Agora que conseguimos remover nós da lista, podemos fazer uma função para montar a árvore de huffman. Esta função recebe o ponteiro para a lista ordenada e, enquanto o tamanho da lista for maior que 1, remove os dois primeiros nós, cria um terceiro nó com a soma da frequência dos dois nós removidos, insere os dois nós removidos como filho deste novo nó e o insere novamente na lista. As figuras a seguir apresentam de forma visual este processo.
O trecho de código a seguir implementa esta lógica. Ao final, a função retorna o ponteiro para o primeiro e único nó da lista, que é a raiz da árvore de huffman.
/* Procedimento para montar a árvore de huffman. */ No* montar_arvore(Lista *lista){ No *primeiro, *segundo, *novo; while(lista->tam > 1){ primeiro = remove_no_inicio(lista); segundo = remove_no_inicio(lista); novo = malloc(sizeof(No)); if(novo){ novo->caracter = '+'; novo->frequencia = primeiro->frequencia + segundo->frequencia; novo->esq = primeiro; novo->dir = segundo; novo->proximo = NULL; inserir_ordenado(lista, novo); } else{ printf("\n\tERRO ao alocar memoria em montar_arvore!\n"); break; } } return lista->inicio; }
Por fim, podemos também implementar um procedimento para imprimir a árvore de huffman na tela, como feito a seguir. Além de imprimir o conteúdo da árvore, imprime também a altura de cada nó da árvore.
/* Procedimento para imprimir na tela a árvore de huffman. */ void imprimir_arvore(No *raiz, int tam){ if(raiz->esq == NULL && raiz->dir == NULL) printf("\tFolha: %c\tAltura: %d\n", raiz->caracter, tam); else{ imprimir_arvore(raiz->esq, tam + 1); imprimir_arvore(raiz->dir, tam + 1); } }
Código parcial para o Algoritmo de Huffman – Parte III – Árvore de Huffman
/* Código escrito por Wagner Gaspar Outubro de 2021 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <locale.h> #define TAM 256 typedef struct no{ unsigned char caracter; int frequencia; struct no *esq, *dir, *proximo; }No; typedef struct{ No *inicio; int tam; }Lista; //----------- parte 1: tabela de frequência ---------------------- void inicializa_tabela_com_zero(unsigned int tab[]){ int i; for(i = 0; i < TAM; i++) tab[i] = 0; } void preenche_tab_frequencia(unsigned char texto[], unsigned int tab[]){ int i = 0; while(texto[i] != '\0'){ tab[texto[i]]++; i++; } } void imprime_tab_frequencia(unsigned int tab[]){ int i; printf("\tTABELA DE FREQUENCIA\n"); for(i = 0; i < TAM; i++){ if(tab[i] > 0) printf("\t%d = %u = %c\n", i, tab[i], i); } } //----------- parte 2: Lista Encadeada Ordenada ---------------------- void criar_lista(Lista *lista){ lista->inicio = NULL; lista->tam = 0; } void inserir_ordenado(Lista *lista, No *no){ No *aux; // a lista está vazia? if(lista->inicio == NULL){ lista->inicio = no; //lista->tam++; } // tem frequência menor que o início da lista else if(no->frequencia < lista->inicio->frequencia){ no->proximo = lista->inicio; lista->inicio = no; //lista->tam++; } else{ aux = lista->inicio; while(aux->proximo && aux->proximo->frequencia <= no->frequencia) aux = aux->proximo; no->proximo = aux->proximo; aux->proximo = no; //lista->tam++; } lista->tam++; } void preencher_lista(unsigned int tab[], Lista *lista){ int i; No *novo; for(i = 0; i < TAM; i++){ if(tab[i] > 0){ novo = malloc(sizeof(No)); if(novo){ novo->caracter = i; novo->frequencia = tab[i]; novo->dir = NULL; novo->esq = NULL; novo->proximo = NULL; inserir_ordenado(lista, novo); } else{ printf("\tERRO ao alocar memoria em preencher_lista!\n"); break; } } } } void imprimir_lista(Lista *lista){ No *aux = lista->inicio; printf("\n\tLista ordenada: Tamanho: %d\n", lista->tam); while(aux){ printf("\tCaracter: %c Frequência: %d\n", aux->caracter, aux->frequencia); aux = aux->proximo; } } //------------- parte 3: Montar a Árvore de Huffman ---------------------- No* remove_no_inicio(Lista *lista){ No *aux = NULL; if(lista->inicio){ aux = lista->inicio; lista->inicio = aux->proximo; aux->proximo = NULL; lista->tam--; } return aux; } No* montar_arvore(Lista *lista){ No *primeiro, *segundo, *novo; while(lista->tam > 1){ primeiro = remove_no_inicio(lista); segundo = remove_no_inicio(lista); novo = malloc(sizeof(No)); if(novo){ novo->caracter = '+'; novo->frequencia = primeiro->frequencia + segundo->frequencia; novo->esq = primeiro; novo->dir = segundo; novo->proximo = NULL; inserir_ordenado(lista, novo); } else{ printf("\n\tERRO ao alocar memoria em montar_arvore!\n"); break; } } return lista->inicio; } void imprimir_arvore(No *raiz, int tam){ if(raiz->esq == NULL && raiz->dir == NULL) printf("\tFolha: %c\tAltura: %d\n", raiz->caracter, tam); else{ imprimir_arvore(raiz->esq, tam + 1); imprimir_arvore(raiz->dir, tam + 1); } } int main() { unsigned char texto[] = "Vamos aprender programação"; unsigned int tabela_frequencia[TAM]; Lista lista; No *arvore; int colunas; char **dicionario; char *codificado, *decodificado; setlocale(LC_ALL, "Portuguese"); //----------- parte 1: tabela de frequencia --------------- inicializa_tabela_com_zero(tabela_frequencia); preenche_tab_frequencia(texto, tabela_frequencia); imprime_tab_frequencia(tabela_frequencia); //----------- parte 2: Lista Encadeada Ordenada ----------- criar_lista(&lista); preencher_lista(tabela_frequencia, &lista); imprimir_lista(&lista); //----------- parte 3: Montar a Árvore de Huffman --------- arvore = montar_arvore(&lista); printf("\n\tÁrvore de Huffam\n"); imprimir_arvore(arvore, 0); return 0; }