Estruturas de Dados

Estruturas de dados fundamentais em C: lista encadeada, pilha e fila.

📖 O que você vai aprender nesta página
✅ O essencial para saber
  • Lista encadeada: cada nó aponta para o próximo
  • Pilha: último a entrar, primeiro a sair (LIFO)
  • Fila: primeiro a entrar, primeiro a sair (FIFO)
⭐ Leia se tiver tempo
  • Árvores e tabelas hash
  • Complexidade: O(n), O(1)
  • Comparação com a STL / bibliotecas padrão

O que é uma Estrutura de Dados? — Projetando Como os Dados são Organizados

Uma estrutura de dados define como os dados são dispostos na memória e como você trabalha com eles. Arrays são em si uma estrutura de dados, mas várias situações tornam o uso exclusivo de arrays complicado.

Limitações de arrays

Inserção no meio/início lenta
Inserir perto do início força todo elemento seguinte a deslocar. Com um milhão de itens, são um milhão de deslocamentos.
Tamanho fixo
O tamanho fica travado na hora da declaração, o que torna dados que crescem e encolhem difíceis de lidar (malloc ajuda, mas é trabalho extra).

Estruturas de dados comuns

EstruturaCaracterísticaExemplo de uso
Lista encadeadaNós ligados por ponteiros; inserção/remoção rápidaDados ordenados com inserção/remoção frequente
PilhaÚltimo a entrar, primeiro a sair (LIFO)Chamadas de função, histórico de Desfazer
FilaPrimeiro a entrar, primeiro a sair (FIFO)Fila de impressão, agendamento de tarefas
Todas estas são construídas a partir de structs, ponteiros e malloc. Esta aula junta tudo que aprendemos até aqui!

Lista Encadeada — Uma Cadeia Ligada por Ponteiros

Uma lista encadeada é uma sequência em que cada elemento (um nó) guarda algum dado mais um ponteiro para o próximo nó.

Definição do nó

typedef struct Node {
  int data;              // o dado armazenado
  struct Node *next;     // ponteiro para o próximo nó
} Node;

Imagem na memória

10
next→
20
next→
30
next→
NULL

Operações principais

// (1) Criar um nó
Node* createNode(int val) {
  Node *n = malloc(sizeof(Node));
  n->data = val;
  n->next = NULL;
  return n;
}

// (2) Inserir no início (O(1) — rápido!)
void pushFront(Node **head, int val) {
  Node *n = createNode(val);
  n->next = *head;     // next do novo nó = head antigo
  *head = n;           // head agora aponta para o novo nó
}

// (3) Imprimir todos os elementos
void printList(Node *head) {
  for (Node *p = head; p != NULL; p = p->next)
    printf("%d → ", p->data);
  printf("NULL\n");
}

// (4) Liberar a memória (esquecer = vazamento!)
void freeList(Node *head) {
  while (head) {
    Node *tmp = head->next;
    free(head);
    head = tmp;
  }
}
Inserção no início
O(1) — muito rápido
Busca
O(n) — percorre desde o head
Vs. arrays
Sem acesso por índice; acesso aleatório não é o forte.

Pilha — Último a Entrar, Primeiro a Sair (LIFO)

Uma pilha remove primeiro o item mais recentemente empilhado. Pense em empilhar pratos. A pilha de chamadas que rastreia chamadas de função funciona do mesmo jeito.
Visualização de pilha
10
20
30
← TOPO (itens saem por aqui)
// push: adiciona no topo
void push(Node **top, int val) {
  Node *n = createNode(val);
  n->next = *top;
  *top = n;
}

// pop: remove do topo
int pop(Node **top) {
  if (*top == NULL) return -1;
  Node *tmp = *top;
  int val = tmp->data;
  *top = tmp->next;
  free(tmp);
  return val;
}
Exemplos do dia a dia: o botão Voltar do navegador, Ctrl+Z (Desfazer) e a pilha de chamadas que rastreia chamadas de função — todos usam uma pilha por baixo dos panos.

Fila — Primeiro a Entrar, Primeiro a Sair (FIFO)

Uma fila remove itens na mesma ordem em que entraram — igual a uma fila de caixa.
Visualização de fila
Sai ←
10
20
30
→ Entra
// enqueue: adiciona na cauda
void enqueue(Node **head, Node **tail, int val) {
  Node *n = createNode(val);
  if (*tail) (*tail)->next = n;
  else *head = n;
  *tail = n;
}

// dequeue: remove da cabeça
int dequeue(Node **head, Node **tail) {
  if (!*head) return -1;
  Node *tmp = *head;
  int val = tmp->data;
  *head = tmp->next;
  if (!*head) *tail = NULL;
  free(tmp);
  return val;
}
Exemplos do dia a dia: filas de impressão, escalonadores de tarefas do SO e ordenação de mensagens de chat.

Array vs Lista encadeada vs Pilha vs Fila

OperaçãoArrayLista encadeadaPilhaFila
Inserir no inícioO(n)O(1)
Acrescentar no fimO(1)O(1)*O(1)O(1)
Acesso aleatórioO(1)O(n)
Inserir no meioO(n)O(1)†
* com um ponteiro de cauda. † quando o ponto de inserção é conhecido.

Demo de Lista Encadeada

Use os botões para adicionar e remover elementos de uma lista encadeada e veja a estrutura atualizar em tempo real.
Estado da lista:
(lista vazia)
Pressione um botão para manipular a lista.

Aulas relacionadas

Avançado
malloc / free
Alocação dinâmica e como evitar vazamentos de memória.
Avançado
Fundamentos de Ponteiros
Entenda ponteiros com visualização de memória.
Algoritmos
Algoritmos de Ordenação
Bubble sort, selection sort e insertion sort, animados.
← Aula anterior
Aula 30: Memória Dinâmica (malloc/free)
Próxima aula →
Aula 32: Algoritmos de Ordenação

Quiz Rápido

Teste seu entendimento desta aula.

Q1. Qual a vantagem de uma lista encadeada?

Acesso aleatório rápido
Inserção e remoção eficientes
Usa menos memória

Inserção e remoção em uma lista encadeada só reconectam ponteiros. Arrays precisam deslocar elementos, o que é lento.

Q2. Em que ordem uma pilha retorna os itens?

FIFO (primeiro a entrar, primeiro a sair)
LIFO (último a entrar, primeiro a sair)
Aleatório

Uma pilha é LIFO (Last In, First Out) — o item mais recentemente empilhado é o primeiro a ser desempilhado.

Q3. Em que ordem uma fila retorna os itens?

FIFO (primeiro a entrar, primeiro a sair)
LIFO (último a entrar, primeiro a sair)
Ordem de prioridade

Uma fila é FIFO (First In, First Out) — igual a uma fila de caixa.

Compartilhe este artigo
Compartilhar no X Compartilhar no Facebook