Dando continuidade à elaboração do Algoritmo de Huffman, vamos desenvolver nesta aula o passo 2: elaborar uma lista encadeara ou uma fila de prioridade ordenadas com os elementos da tabela de frequência.
Como vamos criar uma lista / fila, precisamos pensar como implementar estas estruturas. Está lembrado das opções que apresentamos na segunda aula sobre o Algoritmo de Huffman?
Nesta abordagem irei utilizar a terceira opção, um único nó que pode ser utilizado para nossa lista / fila de prioridade (ordenada de forma crescente) e para a árvore binária.
O trecho de código a seguir implemente este nó. Ele possui um campo caracter, um campo inteiro para a frequência e três ponteiros, um que será usado na lista / fila para apontar o próximo nó, e outros dois que serão utilizados na construção da árvore binária, para os filhos à esquerda e direita.
/* Estrutura do nó utilizado na elaboração da fila e da árvore */ typedef struct no{ unsigned char caracter; int frequencia; struct no *esq, *dir, *proximo; }No;
A partir de agora irei adotar o uso de uma lista encadeada ordenada. Assim, sempre farei referência à estrutura lista e não mais lista / fila. Para definir esta estrutura vamos utilizar o trecho de código a seguir que possui um ponteiro para um nó, definido acima, e um inteiro para o tamanho da lista.
/* Nó para a estrutura lista encadeada */ typedef struct{ No *inicio; int tam; }Lista;
Nossa lista será criada na função main e no início ela deve estar vazia. Assim, o primeiro procedimento que faremos será para inicializar a lista, atribuindo null ao ponteiro e zero ao tamanho.
/* Procedimento para inicializar uma lista */ void criar_lista(Lista *lista){ lista->inicio = NULL; lista->tam = 0; }
A nossa lista não é simplesmente uma lista encadeada, é uma lista encadeada ordenada. Assim, precisamos criar um procedimento para fazer a inserção de forma ordenada. Este procedimento receberá o ponteiro para a lista e um ponteiro para o nó a ser inserido. Lembre-se que os nós serão formados a partir da tabela de frequência.
/* Procedimento para inserir ordenado na lista encadeada */ void inserir_ordenado(Lista *lista, No *no){ No *aux; // a lista está vazia? if(lista->inicio == NULL){ lista->inicio = no; } // tem frequência menor que o início da lista else if(no->frequencia < lista->inicio->frequencia){ no->proximo = lista->inicio; lista->inicio = no; } else{ // insere no meio ou no fim da lista aux = lista->inicio; while(aux->proximo && aux->proximo->frequencia <= no->frequencia) aux = aux->proximo; no->proximo = aux->proximo; aux->proximo = no; } lista->tam++; // incrementa o tamanho da lista }
Agora que já temos nossa estrutura lista, precisamos percorrer a tabela de frequência e, para toda posição cuja frequência seja maior que zero, precisamos criar um nó, preencher suas informações e inseri-lo na nossa lista ordenada, como feito a seguir.
/* Procedimento para preencher a lista a partir da tabela de frequência */ void preencher_lista(unsigned int tab[], Lista *lista){ int i; No *novo; for(i = 0; i < TAM; i++){ // percorre toda a tabela if(tab[i] > 0){ // se quantidade for maior que zero 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; } } } }
Também é interessante imprimir ao menos parte da lista na tela a fim de conferir se realmente está na ordem crescente. Assim, podemos criar o procedimento a seguir que irá imprimir o caracter e a frequência de cada nó da lista encadeada na tela.
/* Procedimento para imprimir uma lista encadeada na tela */ 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; } }
Código parcial para o Algoritmo de Huffman – Parte II – Lista Ordenada
/* 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; } // tem frequência menor que o início da lista else if(no->frequencia < lista->inicio->frequencia){ no->proximo = lista->inicio; lista->inicio = no; } else{ aux = lista->inicio; while(aux->proximo && aux->proximo->frequencia <= no->frequencia) aux = aux->proximo; no->proximo = aux->proximo; aux->proximo = no; } 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; } } 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); return 0; }