Dados
Pilhas (Stacks)
Prof. Leandro C. Fernandes
Estruturas de Dados
–Estruturas de Dados
Pilhas
Definição
Estrutura para armazenar um conjunto de elementos que funciona da seguinte forma:
Novos elementos sempre entram no “topo” da pilha
O único elemento que se pode retirar da pilha em um dado momento é o elemento do topo
Para que serve
Para modelar situações em que é preciso “guardar para mais tarde” vários elementos e “lembrar” sempre do último elemento armazenado
L.I.F.O.
São também conhecidas “Last In, First Out”
Estruturas de Dados
Pilhas
Def. informal:
Lista linear em que inserção e eliminação de elementos só ocorrem em uma das
extremidades (TOPO da pilha)
Característica:
Dada uma Pilha P = (a1, a2, ..., an), dizemos que a1 é o elemento na base; an é o elemento do
topo, e ai+1 está acima de ai na pilha
Topo D
C B
Base A
–Estruturas de Dados
Operações
Operações principais:
Push(P,X): empilha (insere) o valor da variável X na pilha P Pop(P): desempilha (remove) e retorna o valor do elemento
que estava no topo de P
Operações auxiliares:
Top(P): acessa (retorna) o valor do elemento do topo de P, sem desempilhar
Size(P): retorna o número de elementos armazenados na pilha P.
Create(P): cria uma pilha vazia P
IsEmpty(P): retorna true se a pilha estiver vazia; false caso contrário
Empty(P): esvazia uma pilha P
Estruturas de Dados
Aplicações de Pilhas
Aplicações diretas:
Histórico de páginas visitadas em um navegador na Web.
Controle de Desfazer/Refazer ações em um editor de textos.
Encadeamento de chamadas a métodos ou funções em ambientes de execução, como em Pascal, C ou Java.
Aplicações indiretas:
Estrutura auxiliar para algoritmos (por ex. ordenação).
Componente de outras estruturas de dados.
–Estruturas de Dados
Exemplo: Pilha de Chamadas
Problema
Um computador está executando a rotina X e, durante a execução de X, encontra uma chamada à rotina Y
Para-se a execução de X e se inicia a execução de Y
Quando se termina a execução de Y, o computador deve saber o que fazer, isto é, onde voltar na rotina X
Dificuldade
O que estava sendo executado quando uma sub-rotina foi
interrompida? Para onde voltar agora que se chegou ao fim de uma subrotina?
Solução
A cada chamada de sub-rotina, armazenar o endereço de retorno (rotina e número da linha, por exemplo)
Como armazenar o endereço de retorno de chamadas sucessivas: pilha
Estruturas de Dados
Exemplo: Pilha de Chamadas
Um programa executável mantém
controle da cadeia de chamadas ativas através de uma Pilha:
Por exemplo, pilha de recursão
Quando uma rotina é chamada, insere- se na pilha um frame com:
Variáveis locais e valor de retorno.
Contador de programa (PC) que
mantém a trilha das declarações sendo executadas.
Quando uma rotina encerra sua
execução o frame é removida da pilha e o controle é passado à rotina no topo da pilha.
–Estruturas de Dados
Exemplo: Expressões
Avaliação de expressões
aritméticas expressas na forma Posfixa:
Expressão: A/B + D*E
Objetivo: ((A/B) + (D*E))
Como ocorre: AB / DE * +
Interpretação da notação usando pilha:
Empilhar operandos até encontrar um operador
Retirar os operandos, calcular e empilhar o resultado
Até que se chegue ao final da expressão
Estruturas de Dados
Implementação Seqüencial
Implementação simples.
Inserções e remoções realizadas no fim da estrutura (posição topo).
Uma variável mantém o controle da posição do topo, e pode ser utilizada também para informar o número de elementos na pilha.
1 2 3 4 5 n-4 n-3 n-2 n-1
...
–Estruturas de Dados
Implementação Seqüencial
typedef int tDado;
typedef struct Pilha { tDado dados[];
int topo;
int MAX;
};
Estruturas de Dados
Implementação Seqüencial
void create(Pilha *p, int tam){
dados = (tDados *) malloc(tam*sizeof(tDados));
p->MAX = tam;
p->topo = -1;
}
void empty(Pilha *p){
p->topo = -1;
}
–Estruturas de Dados
Implementação Seqüencial
int isEmpty(Pilha *p){
return (p->topo == -1) ? 1 : 0 ; }
int isFull(Pilha *p){
return (p->topo == p->MAX-1) ? 1 : 0
; }
Estruturas de Dados
Implementação Seqüencial
tDado top(Pilha *p){
return p->dados[p->topo];
}
int size(Pilha *p){
return p->topo+1;
}
–Estruturas de Dados
Implementação Seqüencial
int push(Pilha *p, tDado x){
if (isFull(p)) return 0;
else {
p->topo++;
p->dados[p->topo] = x;
return 1;
} }
Estruturas de Dados
Implementação Seqüencial
int pop(Pilha *p, tDado *x){
if (isEmpty(p)) return 0;
else {
*x = p->dados[p->topo];
p->topo--;
return 1;
} }
–Estruturas de Dados
Implementação Dinâmica
O topo pode ser o início da lista
A implementação se torna mais eficiente, pois não há necessidade de percorrer a lista.
Não é necessidade de utilizar listas duplamente ligadas.
Pode-se implementar as operações utilizando a biblioteca de lista ligada simples.
Estruturas de Dados
Implementação Dinâmica
typedef int tDado;
typedef struct elem * ptrElem;
typedef struct elem { tDado dado;
ptrElem prox;
};
typedef struct Pilha {
ptrElem topo; /* sempre o primeiro elem da lista */
int qtdeElem;
};
–Estruturas de Dados
Seqüencial vs Dinâmica
Problema da implementação seqüencial de listas:
necessidade de movimentações de itens em inserções e remoções
No caso das pilhas, tais movimentações não ocorrem!
Alocação seqüencial vantajosa na maioria das situações
Alocação dinâmica interessante para pilhas cujo tamanho não pode ser antecipado, ou sofre muita variação
Estruturas de Dados
Seqüencial vs Dinâmica
Operação Estática Dinâmica
Create O(1) O(1)
Push O(1) O(1)
Pop O(1) O(1)
Top O(1) O(1)
Empty O(1) O(1)
Size O(1) O(1)
–Estruturas de Dados
Exercícios
Implementar operações da pilha
void Create(Pilha P)
boolean IsEmpty(Pilha P)
void Empty(Pilha P)
void Push(Pilha P, tDado X)
boolean Pop(Pilha P, tDado X)
tDado Top(Pilha P)
Atenção (considerações sobre TAD)
Parâmetros, mensagens de erro
Estruturas de Dados
Exercícios
Implementar rotina para verificar se duas pilhas são iguais
Implementar rotina para inverter a posição dos elementos de uma pilha
Fazer algoritmo de conversão decimal para binário usando pilha
Implemente uma função que calcule o valor de uma expressão posfixa passada por
parâmetro utilizando o TAD pilha
(resolvido)–Estruturas de Dados
Exercícios: Resolução
float valor(expressao E) { elem x,op1,op2;
Pilha P;
create(&P);
while (!acabou(E)) { x = proxSimb(E);
if (isOperando(x)) push(&P,x)
else {
pop(&P,&op1);
pop(&P,&op2);
push( calcula(op1,x,op2) );
} }
return top(&P);
}
Estruturas de Dados
Exercícios
Considere que um editor de texto representa os caracteres digitados como uma pilha, sendo que o último caractere lido fica no topo
Alguns comandos apagam caracteres. Por exemplo, o backspace apaga o último caractere lido e aguns comandos apagam tudo o que já foi lido
anteriormente
Considere que # representa backspace e @ indica “apagar tudo”
Faça um programa que execute essas ações usando o TAD pilha (resolvido)
–Estruturas de Dados
Exercícios: Resolução
void main() { Pilha P;
char c;
create(&P);
c = getch();
while (c != ‘\n’) { if (c ==‘#’)
pop(&P,&c)
else if (c ==‘@’) empty(&P) else
push(&P,c);
c = getch();
} }