Pilha
1. O que são Pilhas
Section titled “1. O que são Pilhas”Uma pilha é um Tipo Abstrato de Dados que implementa o comportamento LIFO (“Last In, First Out”), isto é, o último elemento que entrou na estrutura será o primeiro a sair. Na vida real, é como uma pilha de pratos: você empilha um em cima do outro e, na hora de retirar, pega sempre o prato que está por cima.
“Murilão, denovo mais uma estrutura? Por que tantas?”
Aqui você tem uma ótima pergunta! Trabalhar com diferentes estruturas, nos permite implementar comportamentos específicos por cada uma das estruturas. O que ganhamos com isso? Estruturas mais especializadas para realizar suas tarefas. Fica mais fácil de compreender como ela está funcionando e também nos permite manter melhor esse código.
As operações fundamentais de uma pilha são:
- push(elemento): insere um novo elemento no “topo” da pilha.
- pop() → elemento: remove e retorna o elemento que está no topo.
- top() → elemento (ou peek): consulta o elemento no topo sem removê-lo.
- isEmpty() → booleano: verifica se a pilha não contém elementos.
- size() → inteiro: retorna quantos elementos estão atualmente empilhados.
2. Por que usar uma pilha?
Section titled “2. Por que usar uma pilha?”Vamos la, vamos ver algumas das aplicações que podemos desenvolver utilizando essa estrutura. Pessoal vale lembrar que podemos utilizar esse tipo abstrato quando for interessante utilizar este comportamento!
- Controle de fluxo em chamadas de função: a pilha de chamadas armazena cada frame de execução e desempilha quando a função retorna.
- Desfazer/refazer (undo/redo): em editores de texto ou gráficos, cada ação é empilhada para que possa ser revertida na ordem inversa.
- Avaliação de expressões e parsing: compiladores usam pilhas para converter e avaliar notações infix, prefix ou pós-fix.
- Algoritmos de backtracking: como DFS em grafos ou soluções de labirintos, empilha-se uma escolha e desempilha-se quando for preciso “voltar atrás”.
A abstração de pilha permite que você se concentre em o que precisa empilhar e desempilhar, sem expor detalhes de como a memória ou o vetor subjacente está sendo manipulado. Isso torna o código mais claro, flexível e fácil de manter.
3. Pseudocódigo das funções
Section titled “3. Pseudocódigo das funções”Pessoal aqui vamos dar uma olhada em como são as funções que podem ser implementadas com as pilhas.
# Exemplo 1: Pilha com vetor (Array-Based Stack)
TAD PilhaArray CONSTANTES: MAX ← 100 # capacidade máxima da pilha
ATRIBUTOS: dados: vetor[0..MAX-1] de Elemento topo: inteiro # índice do próximo slot livre
MÉTODOS:
criar() -> PilhaArray pilha.topo ← 0 RETORNAR pilha
isEmpty() -> booleano RETORNAR (pilha.topo = 0)
isFull() -> booleano RETORNAR (pilha.topo = MAX)
push(elem: Elemento) SE isFull() ESCREVER "Erro: pilha cheia" RETORNAR pilha.dados[pilha.topo] ← elem pilha.topo ← pilha.topo + 1
pop() -> Elemento SE isEmpty() ESCREVER "Erro: pilha vazia" RETORNAR nulo pilha.topo ← pilha.topo - 1 RETORNAR pilha.dados[pilha.topo]
top() -> Elemento SE isEmpty() ESCREVER "Erro: pilha vazia" RETORNAR nulo RETORNAR pilha.dados[pilha.topo - 1]
size() -> inteiro RETORNAR pilha.topoVamos agora analisar alguns pontos desse pseudocódigo:
- A implementação é realizada com vetor.
- Criar a pilha, inicia seu topo com o valor zero.
- A operação de
push, se a pilha não estiver cheia, adiciona o elemento no final da pilha. - A operação de
popretira o elemento da pilha, o último que foi adicionado.
4. Implementação em C
Section titled “4. Implementação em C”Agora, vamos ver a implementação do código em C:
#include <stdio.h>#include <stdbool.h>
#define MAX 100 // capacidade máxima da pilha
// Tipo PilhaArray armazenando inteirostypedef struct { int dados[MAX]; int topo; // índice do próximo slot livre} Pilha;
// Inicializa a pilha vaziavoid criarPilha(Pilha *p) { p->topo = 0;}
// Retorna true se a pilha estiver vaziabool isEmpty(Pilha *p) { return (p->topo == 0);}
// Retorna true se a pilha estiver cheiabool isFull(Pilha *p) { return (p->topo == MAX);}
// Insere elemento no topobool push(Pilha *p, int x) { if (isFull(p)) { printf("Erro: pilha cheia\n"); return false; } p->dados[p->topo++] = x; return true;}
// Remove e retorna o elemento do topo// em *ret; retorna false em underflowbool pop(Pilha *p, int *ret) { if (isEmpty(p)) { printf("Erro: pilha vazia\n"); return false; } *ret = p->dados[--p->topo]; return true;}
// Consulta o topo sem removerbool top(Pilha *p, int *ret) { if (isEmpty(p)) { printf("Erro: pilha vazia\n"); return false; } *ret = p->dados[p->topo - 1]; return true;}
// Retorna o número de elementos na pilhaint size(Pilha *p) { return p->topo;}
// Exemplo de usoint main() { Pilha pilha; criarPilha(&pilha);
// Empilhando valores push(&pilha, 10); push(&pilha, 20); push(&pilha, 30);
printf("Tamanho após pushes: %d\n", size(&pilha)); // deve imprimir 3
// Consultando o topo int v; if (top(&pilha, &v)) printf("Topo atual: %d\n", v); // deve imprimir 30
// Desempilhando valores while (!isEmpty(&pilha)) { pop(&pilha, &v); printf("Pop: %d | Tamanho agora: %d\n", v, size(&pilha)); }
// Tentando pop em pilha vazia pop(&pilha, &v); // imprime "Erro: pilha vazia"
return 0;}Importante destacar aqui que essa pilha foi implementada para números inteiros. Contudo, podemos utilizar o mesmo princípio para construir pilhas para outros tipos de dados.
5. Exercícios
Section titled “5. Exercícios”5.1: Histórico de Navegação Web
Section titled “5.1: Histórico de Navegação Web”Contexto: Um navegador mantém o histórico de páginas visitadas para permitir “Voltar” e “Avançar”. Tarefa:
-
Modele o TAD
PilhaHistoricoonde cada elemento guardaurl(texto) etimestamp. -
Defina operações:
push(pagina)pop() -> paginatop() -> paginaisEmpty() -> booleanosize() -> inteiro
-
Escreva pseudocódigo para implementar cada operação.
-
Simule: visita “A”, “B”, “C”; dá 2 cliques em “Voltar” (pop); mostra a página atual (top).
5.2: Validação de Parênteses em Expressões
Section titled “5.2: Validação de Parênteses em Expressões”Contexto: Um compilador verifica se todos os parênteses, chaves e colchetes estão corretamente balanceados. Tarefa:
-
Crie TAD
PilhaChar. -
Escreva função
validar(expressao: texto) -> booleanoque:- Percorre cada caractere; ao abrir “( { [” faz
push; ao fechar “) } ]” fazpope compara tipo. - Em qualquer inconsistência, retorna
false.
- Percorre cada caractere; ao abrir “( { [” faz
-
Demonstre com as expressões “([])”, “([)]” e “((())”.
5.3: Undo/Redo em Editor de Texto
Section titled “5.3: Undo/Redo em Editor de Texto”Contexto: Um editor permite desfazer (undo) e refazer (redo) operações de digitação.
Tarefa:
-
Modele duas pilhas:
PilhaUndoePilhaRedo, cada elemento contendo o texto inserido/removido. -
Defina operações:
digitar(acao)→pushemUndo, limpaRedoundo()→popdeUndoepushemRedoredo()→popdeRedoepushemUndo
-
Escreva pseudocódigo para estas funções.
-
Simule: digita “A”, “B”; undo; digita “C”; redo (não deve refazer); undo.
5.4: Avaliação de Expressões Pós-fixadas (RPN)
Section titled “5.4: Avaliação de Expressões Pós-fixadas (RPN)”Contexto: Calculadoras RPN (Reverse Polish Notation) usam pilha para avaliar expressões. Tarefa:
-
Modele
PilhaNumdedouble. -
Escreva função
avaliarRPN(tokens: lista<string>) -> númeroque:- Ao ler número faz
push, ao ler operador ( + - * / ) fazpopduas vezes, aplica epushresultado.
- Ao ler número faz
-
Dê pseudocódigo e avalie as expressões
["2","3","+","4","*"]e["5","1","2","+","4","*","+","3","-"].
5.5: Pilha de Chamadas de Funções (Call Stack)
Section titled “5.5: Pilha de Chamadas de Funções (Call Stack)”Contexto: Em tempo de execução, cada chamada de função é empilhada; ao retornar, desempilha-se. Tarefa:
- Modele
PilhaFrameonde cadaFramecontémfuncao(texto) eretorno(endereço). - Defina
pushFrame,popFrame,topFrame. - Escreva pseudocódigo que simule as chamadas:
main→A→B→ retorna →C→ retorna → retorna. - Exiba a pilha após cada operação.
5.6: Conversão de Base Numérica
Section titled “5.6: Conversão de Base Numérica”Contexto: Para converter um número decimal em binário, usa-se pilha para armazenar restos. Tarefa:
-
Modele
PilhaInt. -
Escreva função
converterParaBase(n: inteiro, base: inteiro) -> stringque:- Enquanto
n > 0, fazpush(n % base)en /= base. - Depois, pop todos os restos para formar a representação.
- Enquanto
-
Demonstre convertendo
n=45para base2e16.
5.7: Verificação de Rotas em Labirinto
Section titled “5.7: Verificação de Rotas em Labirinto”Contexto: Um algoritmo de backtracking empilha posições visitadas para explorar caminhos e desempilha ao voltar. Tarefa:
-
Modele
PilhaPosicaocomx, y. -
Escreva pseudocódigo de DFS que:
- Ao mover para próxima célula válida faz
push; - Se chega em beco sem saída, faz
popaté encontrar outra direção.
- Ao mover para próxima célula válida faz
-
Descreva como a pilha cresce e encolhe para um labirinto 3×3 simples.
5.8: Implementação de Undo em Jogo de Desenho
Section titled “5.8: Implementação de Undo em Jogo de Desenho”Contexto: Num app de desenho, cada traço (linha) é um objeto empilhado para possibilitar “desfazer”. Tarefa:
- Modele
PilhaTraçoondeTrazadoguarda coordenadas(x1,y1)-(x2,y2). - Defina
adicionarTraco(traco),undoTraco(). - Pseudocódigo para desenhar 5 traços, desfazer 3, adicionar 2 e desfazer 1.
5.9: Avaliação de Notação Infix para Pós-fix
Section titled “5.9: Avaliação de Notação Infix para Pós-fix”Contexto: Algoritmo Shunting-Yard converte expressão infix para postfix usando uma pilha de operadores. Tarefa:
- Modele
PilhaOpde operadores+ - * / ( ). - Escreva pseudocódigo do Shunting-Yard (push em
(, pop até(ao encontrar)etc.). - Converta “3 + 4 * 2 / ( 1 - 5 )”.
5.10: Verificação de Palíndromo
Section titled “5.10: Verificação de Palíndromo”Contexto: Para testar se uma string é palíndroma, empilha-se a primeira metade e compara com a segunda. Tarefa:
-
Modele
PilhaChar. -
Escreva função
ehPalindromo(s: texto) -> booleanoque:- Empilha
s[0..mid-1]; - Se
lenímpar, ignoramid; - Percorre
mid..end, pop e compara.
- Empilha
-
Demonstre com “radar”, “level”, “hello”.