Skip to content

Tabelas de Símbolos (Symbol Tables - ST)


1. Conceitos Gerais

Uma Tabela de Símbolos (ST) é uma estrutura de dados fundamental que armazena pares chave-valor, onde a chave é usada para identificar unicamente um item e o valor contém informações associadas a esse item.

Princípio fundamental: Fornecer operações eficientes para inserção, busca e remoção de itens baseados em suas chaves.


2. Interface Básica

2.1 Definições e Macros

Definições básicas para Tabela de Símbolos:
#define key int
#define less(A, B) ((A) < (B))
#define eq(A, B) ((A) == (B))

typedef struct {
    key k;    // Chave (identificador único)
    data d;   // Dados associados à chave
} Item;

#define key(A) (A.k)  // Macro para acessar a chave de um item

2.2 Operações Básicas

Interface da Tabela de Símbolos:
// Inicializa a tabela
void ST_init();

// Insere um item na tabela
void ST_insert(Item ni);

// Remove um item pela chave
void ST_remove(key r);

// Busca um item pela chave
Item ST_search(key k);

// Verifica se a tabela está vazia
int ST_empty();

// Retorna o número de itens na tabela
int ST_count();

3. Implementações de Tabelas de Símbolos

3.1 Implementação com Vetor Linear

3.1.1 Características

  • Usa um vetor para armazenar itens sequencialmente;
  • st_last indica o número de itens e a próxima posição livre;
  • Ideal para operações simples, mas com desempenho linear para busca e remoção;

3.1.2 Implementação

Implementação com vetor linear:
#define MAX_ITENS 1000000

int st_last;    // Número de itens e próxima posição livre
Item *st;       // Vetor de itens

// Inicializa a tabela
int ST_init() {
    st = malloc(sizeof(Item) * MAX_ITENS);
    if (st == NULL) return 0;  // Falha na alocação
    st_last = 0;
    return 1;  // Sucesso
}

// Insere um item no final do vetor
void ST_insert(Item ni) {
    if (st_last < MAX_ITENS) {
        st[st_last++] = ni;
    }
}

// Verifica se a tabela está vazia
int ST_empty() {
    return st_last == 0;
}

// Retorna o número de itens
int ST_count() {
    return st_last;
}

// Busca linear por uma chave
int ST_search_index(key k) {
    for (int i = 0; i < st_last; i++) {
        if (eq(k, key(st[i]))) {
            return i;  // Retorna o índice do item encontrado
        }
    }
    return -1;  // Não encontrado
}

// Busca e retorna o item
Item ST_search(key k) {
    int index = ST_search_index(k);
    if (index != -1) {
        return st[index];
    }
    // Retorna um item vazio ou trata o erro
    Item empty_item = {0};
    return empty_item;
}

// Remove um item pela chave
void ST_remove(key r) {
    int to_remove = ST_search_index(r);
    if (to_remove != -1) {
        // Move o último item para a posição a ser removida
        st[to_remove] = st[st_last - 1];
        st_last--;
    }
}

// Destrói a tabela liberando a memória
void ST_destroy() {
    free(st);
    st_last = 0;
}

3.1.3 Análise de Complexidade

Operação Complexidade Descrição
ST_insert O(1) Inserção no final do vetor
ST_search O(n) Busca linear no vetor
ST_remove O(n) Busca + movimentação de elementos
ST_count O(1) Acesso direto ao contador

3.2 Implementação com Lista Encadeada

3.2.1 Características

  • Cada nó contém um item e um ponteiro para o próximo nó;
  • Inserções são feitas no início da lista (mais eficiente);
  • Flexível quanto ao tamanho (crescimento dinâmico);

3.2.2 Implementação

Implementação com lista encadeada:
// Estrutura do nó da lista
typedef struct ST_node {
    Item item;
    struct ST_node *next;
} ST_node;

ST_node *st_head;  // Cabeça da lista
int st_count;      // Contador de elementos

// Inicializa a tabela
void ST_init() {
    st_head = NULL;
    st_count = 0;
}

// Insere um item no início da lista
void ST_insert(Item ni) {
    ST_node *new_node = malloc(sizeof(ST_node));
    if (new_node == NULL) return;  // Falha na alocação

    new_node->item = ni;
    new_node->next = st_head;
    st_head = new_node;
    st_count++;
}

// Verifica se a tabela está vazia
int ST_empty() {
    return st_head == NULL;
}

// Retorna o número de itens
int ST_count() {
    return st_count;
}

// Busca por uma chave na lista
Item ST_search(key k) {
    ST_node *current = st_head;

    while (current != NULL) {
        if (eq(k, key(current->item))) {
            return current->item;  // Item encontrado
        }
        current = current->next;
    }

    // Item não encontrado
    Item empty_item = {0};
    return empty_item;
}

// Remove um item pela chave
void ST_remove(key r) {
    ST_node *current = st_head;
    ST_node *prev = NULL;

    while (current != NULL) {
        if (eq(r, key(current->item))) {
            // Item encontrado, remover da lista
            if (prev == NULL) {
                // Remover o primeiro elemento
                st_head = current->next;
            } else {
                prev->next = current->next;
            }

            free(current);
            st_count--;
            return;
        }

        prev = current;
        current = current->next;
    }
}

// Destrói a tabela liberando toda a memória
void ST_destroy() {
    ST_node *current = st_head;
    ST_node *next;

    while (current != NULL) {
        next = current->next;
        free(current);
        current = next;
    }

    st_head = NULL;
    st_count = 0;
}

3.2.3 Análise de Complexidade

Operação Complexidade Descrição
ST_insert O(1) Inserção no início da lista
ST_search O(n) Busca linear na lista
ST_remove O(n) Busca + remoção do nó
ST_count O(1) Acesso direto ao contador

4. Comparação das Implementações

Aspecto Vetor Linear Lista Encadeada
Inserção O(1) O(1)
Busca O(n) O(n)
Remoção O(n) O(n)
Espaço O(n) O(n)
Crescimento Tamanho fixo Dinâmico
Localidade Boa (cache) Ruim
Simplicidade Alta Média