Pilhas
Pilha é um tipo de lista onde todas as operações de inserção e remoção são feitas na mesma extremidade (Topo).
O primeiro a entrar é o último a sair e o último a entrar o primeiro a sair (LIFO – Last-In First-Out).
Trata-se de uma estrutura dinâmica (pode aumentar ou diminuir durante a existência).
Exemplos Físicos:
Suporta três operações básicas:
Top (topo) – acessa o elemento do topo.
Push (empure) – insere um elemento no topo.
Pop (salte) – remove um elemento do topo.
Exemplo de Instruções
Top(P) → acessa o elemento localizado no topo da Pilha P.
Pop(P) → remove e retorna o elemento posicionado no topo da Pilha P (diminui o tamanho da pilha).
Push(P, X) → acrescenta o elemento X no topo da Pilha P (aumenta o tamanho da pilha).
Sai Entra
Topo
Uma pilha de pratos mola
Guardanapo do topo
Um porta-guardanapos
Exemplo do uso das instruções da Pilha (representação linear).
Operação Estado da Pilha Resultado
--- P:[ ] ---
Push(P, a) P:[ a ] ---
Push(P, b) P:[ b, a ] --- Push(P, c) P:[ c, b, a ] ---
Pop(P) P:[ b, a ] c
Pop(P) P:[ a ] b
Push(P, d) P:[ d, a ] --- Push(P, e) P:[ e, d, a ] ---
Top(P, d) P:[ e, d, a ] e
Pop(P, d) P:[ d, a ] e
Pop(P, d) P:[ a ] d
Representação gráfica das Pilhas
Exemplos de operações com Pilha e sua representação gráfica.
P:[ a
n, a
n – 1,..., a
2,a
1]
an an - 1
a1
...
a2
topo
base
a a
b
a c b
a b
a
Push(P, a) Push(P, b) Push(P, c) Pop(P) Pop(P)
a
d a
e d
a d
a
Push(P, d) Push(P, e) Pop(P) Pop(P) Pop(P)
Vantagens da representação Gráfica
- Gráficos são interpretados mais rapidamente.
- É mais eficiente, pois sugere que nenhum elemento precisa ser deslocado durante a inserção ou remoção.
Limites da Pilha
No mundo real uma pilha é limitada pelo chão e pelo teto.
* Não é possível inserir elementos infinitamente.
* Não é possível remover elementos infinitamente.
No computador os limites da pilha se refere a quantidade de memória usada para representá-la.
Operações Essenciais para uma Pilha
Init: inicializa uma pilha no estado “vazia”; Init(P) IsEmpty: verifica se a pilha está vazia; IsEmpty(P) IsFull: verifica se a pilha está cheia. IsFull(P)
Exemplo em Pascal do uso de Pilhas.
Programa para converter decimal em binário
program Dec_Bin;
uses Pilhas;
var P: Pilha;
x, n: integer;
begin
writeln (‘Digite um inteiro decimal positivo: ’);
readln (n);
Init(P); {torna pilha vazia}
repeat
x:= n mod 2; {calcula o resto}
Push (P, x); {empilha o resto}
n:= n div 2; (calcula quociente) until n=0; {quociente 0, pára}
write (‘Correspondente binário: ’);
while not IsEmpty(P) do {pilha vazia, pára}
begin
x:= Pop(P); {desempilha o resto}
write(x); {imprime o resto}
end;
end.
Pilhas no controle de chamadas e retornos a rotinas
1. program Pilhas;
2. var
3.4. procedure um;
5. begin
6. writeln(‘Um’);
7. dois;
8. end;
9.10. procedure dois;
11. begin
12. writeln(‘Dois’);
13. tres;
14. end;
15.16. procedure tres;
17. begin
18. writeln(‘Três’);
19. end;
20.21. begin 22. um;
23. dois;
24. tres;
25. end.
Seqüência de linhas armazenados na Pilha:
14
8 8 8 14 23 23 23 23 23 24 24 24 25
Exercícios
1. Mostre a situação da pilha P, inicialmente vazia, após a execução de cada uma das operações a seguir:
Push (P, a); Push (P, b); Push (P, c); Push (P, Top (P));
Push (P, Pop (P)); Pop (P); Push (P, e); Pop (P);
1º Resolução (vertical) 2º Resolução (horizontal)
Push (P, a);
a
Push (P, b); b
a
Push (P, c); c
b a
Push (P, Top (P));
c c b a
Push (P, Pop (P));
c c b a
Pop (P); c
b a
Push (P, c);
e c b a
Pop (P); c
b a
2. Usando uma pilha, escreva um programa para ler uma frase e imprimi-la de trás para frente (com as letras invertidas como um espelho).
Push (P, a);
a
Push (P, Pop (P));
a
Push (P, b); b
a
Pop (P);
a
Push (P, c); c
a
Push (P, e); e
c a
Push (P, Top (P));
e e c a
Push (P, Top (P)); c c a
program Inverte;
uses Pilhas;
var P: Pilha;
x, n: integer;
f:string;
begin
writeln ('Entre com uma frase: ');
readln (f);
Init(P); {torna pilha vazia}
for n:=1 to length(f) do push(P, f[n]);
while not IsEmpty(P) do {pilha vazia, para}
write(pop(P));
end.
3. Escreva uma rotina que usa uma pilha para verificar se uma dada cadeia de caracteres é uo não palíndroma. Exemplo: “subinoonibus” é uma palavra palíndroma.
program Palindroma;
uses Pilhas;
var E, S: string;
i: integer;
P: Pinha;
begin
writeln(‘Frase: ’);
readln(E); {obtém frase normal}
Init(P);
for i:=1 to length(E) do {põe caracteres na pilha}
Push(P,S[i]);
S:=‘ ’;
while not IsEmpty(P) do {cria frase invertida}
S:=S+Pop(P);
if E=S then writeln(‘A frase é palíndroma’);
else writeln(‘A frase não é palíndroma’);
end.
Este Exercício exemplifica a função básica de uma pilha: criar funções! Vemos ainda que a função IsEmpty(), em conjunto como comando while, é ideal para codificar um
“looping” que esvazia a pilha, e que o operador +, em Pascal, pode ser usado para anexar um caractere ao final de uma cadeia (ou para concatenar duas cadeias quaisquer!).
4. Considerando as ilustrações a seguir, mostre a sequência de operações Push e Pop que devem ser realizadas sobre as pilhas X, Y e Z para que, partindo do estado inicial, possamos chegar ao estado final.
a)
b)
Resolução A Push(Y, Pop(X)) Push(Z, Pop(X)) Push(Y, Pop(X)) Push(Z, Pop(X)) Push(X, Pop(Y)) Push(Z, Pop(Y)) Push(Z, Pop(X))
Resolução B Push(Y, Pop(X)) Push(Y, Pop(X)) Push(Z, Pop(X)) Push(Y, Pop(X)) Push(Z, Pop(X)) Push(X, Pop(Y)) Push(X, Pop(Y)) Push(Z, Pop(Y)) Push(Z, Pop(X)) Push(Z, Pop(X))
5. Esquematize a situação da pilha de controle de programa, em cada momento durante a execução, para os programas a seguir:
a) c a (X)
b d
(Y) (Z) (X) (Y)
d c (Z)
a b
estado inicial estado final
2 3 (X)
8 4
(Y) (Z) (X) (Y)
1 2 (Z)
3 4
estado inicial estado final
1 8
routine A 1 print “A”
2 call B 3 return
routine B 4 call C 5 call D 6 return
routine C 7 call D 8 print “C”
9 return
routine D 10 print “D”
11 return
b)
Resolução A P: [ ]
P: [3]
P: [5,3]
P: [8,5,3]
P: [5,3]
P: [3]
P: [6,3]
P: [3]
P: [ ]
Resolução B P: []
P: [3]
P: [6,3]
P: [9,6,3]
P: [6,9,6,3]
P: [9,6,9,6,3]
Implementação de Pilhas em Pascal
Características da Pilha:
→ os elementos são armazenados em seqüência.
→ a inclusão e exclusão não requer movimentação de dados.
Por essas características a alocação sequencial é apropriada.
Para tal podem ser usados:
• Vetor: armazena os elementos da Pilha.
• Índice: permite o acesso aos elementos.
Para criar uma pilha pode ser usada a estrutura RECORD:
1. const MAX = 50;
2. type Elem = char;
3. Pilha = record
4. topo: integer;
5. memo: array[1..MAX] of Elem;
6. end;
7. var P: Pilha;
routine A 1 print “A”
2 call B 3 return
routine B 4 print “B”
5 call C 6 return
routine C 7 print “C”
8 call B 9 return
Exemplo de armazenamento da Pilha P[c, b, a]
Algoritmos para Manipulação da Pilha
a) Inicialização da Pilha
procedure Init(var P:Pilha);
begin
P.topo:= 0;
end;
Considerações:
• Não é necessário atribuir valores iniciais aos elementos do vetor ao inicializar a pilha (irrelevante).
• Como o vetor possui elementos entre 1..MAX, não pode haver conteúdo na posição 0.
• P.topo mantém o índice do último elemento inserido.
• O argumento P, recebido pela rotina Init, é passagem por referência (possibilita que qualquer pilha seja inicializada, independentemente de seu nome).
b) Limites da Pilha
function IsEmpty(var P:Pilha): boolean; → Verifica o chão (Topo=0)
begin
if P.topo= 0 then IsEmpty:= true else IsEmpty:= false;
end;
3 a b c ...
P:
1 2 3 4 5 6 7 ... MAX P.memo armazena os elementos da pilha
Base P.topo indica a posição do último elemento inserido
Φ ? ? ? ? ? ... ? ? P:
1 2 3 4 5 6 7 ... MAX
?
P.memo armazena valores indefinidos
P.topo com valor Φ indica que a pilha está vazia, pois não há nenhum elemento nessa posição (Pilha vazia P:[ ]).
function IsFull(var P:Pilha): boolean; → Verifica o chão (Topo=0)
begin
if P.topo = max then IsFull:= true else
IsFull:= false;
end;
Considerações
• Por que usar função no lugar de procedimento?
• Quando a Pilha está vazia?
• Quando a Pilha está cheia?
• A passagem por referência é dada, mas não obrigatória, uma vez que não existe a necessidade de se alterar o valor da Pilha. Entretanto, não é necessário criar uma nova variável para copiar a pilha na memória (quanto > MAX > Tempo) c) Inserindo elementos na Pilha.
procedure Push(var P:Pilha; x:Elem);
begin
if not IsFull(P) then begin
P.topo:= P.topo + 1;
P.memo[P.topo]:= x;
end else
writeln(‘Stack Overflow!’);
end;
Considerações:
• Existe espaço? Se não, ocorre Stack Overflow.
• Inserir o novo elemento no topo.
• Incrementar o valor do topo.
c b a
IsFull (P) verifica se há espaço na pilha
(1)
c b a
P.topo := P.topo + 1 altera a posição do topo da pilha
(2)
c b a
P.memo [ P.topo ] := x armazena o elemento
(3) d
Passos básicos para inserir um elemento na pilha
d) Retirando elementos da Pilha.
procedure Pop(var P:Pilha):Elem;
begin
if not IsEmpty (P) then begin
Pop := P.memo [P.topo];
P.topo:= P.topo - 1;
end else
writeln(‘Stack Overflow!’);
end;
Considerações:
• A Pilha está vazia?
• Remover sempre do topo.
• Retornar o elemento e decrementaro topo.
• Ao tentar retirar um elemento com a Pilha vazia ocorre Stack Underflow.
• A remoção é apenas lógica.
e) Conhecendo o elemento do topo
function Top(var P:Pilha):Elem;
begin
if not IsEmpty(P) then Top:= P.memo[P.topo]
else
writeln (‘Stack Underflow’);
end;
Considerações:
• Não altera o estado da Pilha.
• Retorna o elemento do topo.
Unit Pilhas
unit Pilhas;
interface
c d b a
IsEmpty (P) verifica se há elementos na pilha
(1)
c d b a
P.topo := P.topo + 1 altera a posição do topo da pilha
(2)
c d b a
P.memo [ P.topo ] := x armazena o elemento
(3) Passos básicos para remover um elemento da pilha
const max = 50;
type Elem = char;
Pilha = record
topo: integer;
memo: array[1..max] of Elem;
end;
procedure Init(var P:Pilha);
function IsEmpty(var P:Pilha):boolean;
function IsFull(var P:Pilha):boolean;
procedure Push(var P:Pilha; x:Elem);
function Pop(var P:Pilha):Elem;
function Top(var P:Pilha):Elem;
implementation
procedure Init(var P:Pilha);
begin
P.topo: = 0;
end;
function IsEmpty(var P:Pilha):boolean;
begin
if P.topo= 0 then IsEmpty:= true else IsEmpty:= false;
end;
function IsFull(var P:Pilha):boolean;
begin
if P.topo= max then IsFull:= true else
IsFull:= false;
end;
procedure Push(var P:Pilha; x:Elem);
begin
if not IsFull(P) then begin
P.topo:= P.topo + 1;
P.memo[P.topo]:= x;
end else
writeln (‘Stack Overflow!’);
end;
procedure Pop(var P:Pilha):Elem;
begin
if not IsEmpty(P) then begin
Pop:= P.memo [P.topo];
P.topo:= P.topo - 1;
end else
writeln (‘Stack Overflow!’);
end;
function Top(var P:Pilha):Elem;
begin
if not IsEmpty (P) then Top:= P.memo[P.topo]
else
writeln (‘Stack Underflow!’);
end;
end.
Exercícios:
1. Uma alternativa para organizar os dados de uma pilha consiste no uso de um vetor M[0..n], onde M[0] é usado para manter o índice de topo e M[1], M[2], ..., M[n] são usados para armazenar os elementos contidos na pilha:
a) Que restrição existe ao tipo dos elementos que a pilha poderá contém?
R: Como a pilha é criada a partir de um vetor, os elementos da pilha devem ser todos do mesmo tipo (inteiro, real, char ...) – salvo quando em tipo possa ser convertido em outro tipo.
b) Esquematize a representação gráfica para a pilha P:[d, c, b, a].
c) Implemente as rotinas para inserir e remover elementos da pilha.
2. É possível manter duas pilhas num único vetor, bastando que uma pilha tenha como base a primeira posição do vetor e a outra, a última. As pilhas terão de crescer uma de encontro à outra.
a) Qual a vantagem do esquema de armazenamento, com relação a espaço?
R: Usar duas pilhas com o espaço reservado a apenas uma. Desvantagem: os elementos precisam ser do mesmo tipo.
b) Implemente o tipo Pilha, utilizando esta organização de dados.
5 a b c d ... z y x n-3
1 2 3 4 5 ... n-3 n-2 n-1 n Pilha da esquerda Pilha da direita
d c b a
(P) Base
Topo