1. FEUP/LEEC
Algoritmos e Estruturas de Dados
2001/2002
Pilhas e Filas
João Canas Ferreira
http://www.fe.up.pt/~jcf
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 1
Conteúdo
1. Pilhas
(a) Implementação baseada em listas
(b) Implementação baseada em vectores
2. Filas
(a) Implementação baseada em listas
(b) Implementação baseada em vectores
3. Aplicações de pilhas
(a) Cálculo de expressões RPN
(b) Conversão de expressões inxas para RPN
4. Aplicação de las
(a) Percurso mais curto num labirinto
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 2
1
2. Pilhas
Pilha Estrutura de dados em que a inserção e a remoção de elemen-tos
de uma sequência se faz pela mesma extremidade, geralmente
designada por topo da pilha.
Uma pilha pode ser considerada como uma restrição de lista.
Visto que se trata de uma estrutura de dados mais simples que a lista, é
possível obter implementações mais ecazes.
O conceito de iterador não se aplica a esta estrutura de dados.
LIFO = Last-in First-out
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 3
Operações com pilhas
1 2
1 2 3 4 5
1
3
2
1
4
3
2
1
5
4
3
2
1
5
4
3
2
1
5
4
3
2
1
5 4 3
4
3
2
1
3
2
1
2
2
1
2
1 5 4 3 2 1
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 4
2
3. Pilha – Implementação baseada em listas
template class T class LStack {
public:
LStack();
LStack(const LStack stk);
~LStack();
bool isEmpty() const;
bool isFull() const;
const T top() const;
void makeEmpty();
void pop();
void push(const T x);
T topAndPop();
const LStack operator=(const LStack stk);
///...
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 5
Classe LStack – Secção privada
template class T class LStack {
// ...
private:
class ListNode // classe privada
{ public:
T element;
ListNode *next;
ListNode(const T elem, ListNode *n = 0) : element(elem),
next(n) { };
};
ListNode *topOfStack;
};
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 6
3
4. push() e top()
template class T void LStackT::push(const T x)
{
topOfStack = new ListNode(x, topOfStack);
}
template class T const T LStackT::top() const
{
if (isEmpty()) throw Underflow();
return topOfStack-element;
}
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 7
pop() e topAndPop()
template class T void LStackT::pop()
{
if (isEmpty()) throw Underflow();
ListNode *oldTop = topOfStack;
topOfStack = topOfStack-next;
delete oldTop;
}
template class T T LStackT::topAndPop()
{
T topItem = top();
pop();
return topItem;
}
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 8
4
5. Pilhas–Implementação baseada em vectores
template class T class VStack {
public:
explicit VStack(int capacity = 100);
bool isEmpty() const;
bool isFull() const;
const T top() const;
void makeEmpty();
void pop();
void push(const T x);
T topAndPop();
//...
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 9
VStack – Secção privada
template class T class VStack {
// ...
private:
vectorT theArray;
int topOfStack;
};
Atenção: Não deixar o vector aumentar de tamanho.
topOfStack
= 3
VAZIO
topOfStack
= -1
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 10
5
6. Classe VStack – Implementação
template class T bool VStackT::isFull() const
{
return topOfStack == theArray.size() - 1;
}
template class T void VStackT::makeEmpty()
{
topOfStack = -1;
}
template class T void VStackT::push(const T x)
{
if (isFull()) throw Overflow();
theArray[++topOfStack] = x;
}
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 11
Classe VStack – pop() e top()
template class T const T VStackT::top() const
{
if (isEmpty()) throw Underflow();
return theArray[topOfStack];
}
template class T void VStackT::pop()
{
if (isEmpty()) throw Underflow();
topOfStack--;
}
template class T T VStackT::topAndPop()
{ if (isEmpty()) throw Underflow();
return theArray[topOfStack--];
}
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 12
6
7. Filas
Fila Estrutura de dados em que a inserção e a remoção de elementos
de uma sequência se faz por extremidades opostas, geralmente
designadas por cabeça e cauda da la.
Uma la pode ser considerada como uma restrição de lista.
Visto que se trata de uma estrutura de dados mais simples que a lista, é
possível obter implementações mais ecazes.
O conceito de iterador não se aplica a esta estrutura de dados.
FIFO = First-in First-out
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 13
Funcionamento de las
1 2 3 4 5
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
2 3 4 5
3 4 5
4 5
1 2 3 4 5 5
1 2 3 4 5
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 14
7
8. Filas – Implementação com listas
LQueuechar
back
front
a b c d
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 15
Classe LQueue – secção pública
template class T class LQueue {
public:
LQueue();
LQueue(const LQueue stk);
~LQueue();
bool isEmpty() const;
bool isFull() const;
const T getFront() const;
void makeEmpty();
void dequeue();
void enqueue(const T x);
const LQueue operator=(const LQueue stk);
// ...
};
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 16
8
9. Classe LQueue – secção privada
template class T class LQueue {
// ...
private:
class ListNode
{
public:
T element;
ListNode *next;
ListNode(const T elem, ListNode *n = 0)
: element(elem), next(n) { };
};
ListNode *front, *back;
};
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 17
Classe LQueue – enqueue() e dequeue
template class T void LQueueT::enqueue(const T x)
{
ListNode *oldBack = back;
back = new ListNode(x, 0);
if (oldBack) oldBack-next = back;
if (!front) front = back;
}
template class T void LQueueT::dequeue()
{
if (isEmpty()) throw Underflow();
ListNode *oldFront = front;
front = front-next;
delete oldFront;
if (!front) back = 0;
}
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 18
9
10. Filas – Implementação com vectores
Importante: Evitar deslocar os elementos do vector.
a b c d
front back
b c d
front back
b c d e
front back
tirar ’a’
colocar ’e’
tirar ’b’
c d e
front back
c d e
back front
tirar ’c’
d e
back front
colocar ’f’
f f
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 19
Classe CQueue – secção privada
template class T class CQueue
{
public:
explicit CQueue(int capacity = 100);
// ...
private:
vectorT theArray;
int currentSize;
int front, back;
void increment(int x);
};
O campo currentSize não é estritamente necessário, mas facilita bas-tante
a gestão da estrutura.
Nota: Não se permite a extensão do vector.
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 20
10
11. Classe CQueue – enqueue() e dequeue()
template class Tvoid CQueueT::enqueue(const T x)
{ if (isFull()) throw Overflow();
increment(back);
theArray[back] = x; currentSize++;
}
template class T void CQueueT::increment(int x)
{ if (++x == theArray.size()) x = 0; }
template class T T CQueueT::dequeue()
{ if (isEmpty()) throw Underflow();
currentSize--;
int oldFront = front;
increment(front);
return theArray[oldFront];
}
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 21
Notação RPN
Notação RPN: Expressões aritméticas em que os operadores surgem a
seguir aos operandos (notação pós-xa).
RPN=Reverse Polish Notation
Notação inxa: Os operadores binários surgem entre os operandos.
Notação inxa:
2 * ( 4 + 5 ) / 3
Notação RPN:
2 4 5 + * 3 /
Vantagens de RPN: Não requer parênteses nem regras de precedência.
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 22
11
12. Avaliação de expressões RPN–Exemplo
Uma expressão RPN é facilmente avaliada com a ajuda de uma pilha.
2 4 5 + * 3 /
2 4 5 + * 3 /
2 4 5 + * 3 /
2 4 5 + * 3 /
2 4 5 + * 3 /
2 4 5 + * 3 /
2 4 5 + * 3 /
2
4
2
5
4
2
9
2 18
3
18 6
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 23
Avaliação de expressões RPN–Algoritmo
1. Processar sequencialmente os elementos da expressão.
Para cada elemento:
(a) Se o elemento for um número, colocá-lo na pilha;
(b) Se for um operador
i. Retirar os dois elementos do topo da pilha;
ii. Processar os elementos de acordo com o operador;
iii. Colocar o resultado na pilha.
2. Retirar o (único) elemento da pilha: é o resultado.
(Ver tb. secção 3.3.3 de [DSAA].)
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 24
12
13. Especicação de um programa para avaliar
expressões RPN
1. Ler expressões de cin;
2. Os elementos da expressão são separados por espaços e termina-das
com =;
3. Escrever os resultados em cout;
4. Ignorar linhas vazias;
5. Terminar com m-de-cheiro (^D em Unix, ^Z em Windows)
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 25
Conversão entre notação inxa e RPN
A conversão entre notação inxa e RPN também pode ser feita com o
recurso a uma pilha.
A pilha é usada para guardar os “operadores pendentes”; quando surge
um operador todos os operadores de maior ou igual precedência são
retirados da pilha.
Quando surge um “)” esvazia-se a pilha até encontrar o primeiro “(“.
Prioridades:
(
/ *
+ -
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 26
13
14. Exemplo de conversão inxo RPN
Saída:
2
*
2 * ( 4 + 5 ) / 3 ;
(
*
2 * ( 4 + 5 ) / 3 ;
2 * ( 4 + 5 ) / 3 ;
(
*
4
+
(
*
2 * ( 4 + 5 ) / 3 ;
+
(
*
5
2 * ( 4 + 5 ) / 3 ;
2 * ( 4 + 5 ) / 3 ;
*
+
/
2 * ( 4 + 5 ) / 3 ;
*
/
3 / =
2 * ( 4 + 5 ) / 3 ;
2 * ( 4 + 5 ) / 3 ;
2 * ( 4 + 5 ) / 3 ;
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 27
Especicação do programa de conversão
1. Ler expressões de cin;
2. Os elementos da expressão são separados por espaços e termina-das
com ponto-e-vírgula;
3. Escrever os resultados em cout, terminados com =;
4. Ignorar linhas vazias;
5. Terminar com m-de-cheiro (^D em Unix, ^Z em Windows)
Os dois programas anteriores podem ser combinados em Unix:
# i2r | postfix
2 * ( 3 + 4 ) ;
14
^D
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 28
14
15. Percurso mais curto – Algoritmo de Lee
Como encontrar o percurso mais curto entre dois pontos de um labirin-to?
3 2
2 1
1 a 1 2
2 1 2 b
2 3 4 8 9
5 6 7 8
6 7 8 9
1
2
3
4
5
6
7
1 2 3 4 5 6 7
3 2
2 1
1 a 1 2
2 1 2 b
2 3 4 8 9
5 6 7 8
6 7 8 9
1
2
3
4
5
6
7
1 2 3 4 5 6 7
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 29
Algoritmo de Lee
1. Numerar todos os vizinhos do ponto inicial com 1.
2. Colocar os vizinhos numa la.
3. Enquanto a la não estiver vazia e não se tiver atingido o ponto
de destino:
(a) Retirar um ponto da la;
(b) Numerar os seus vizinhos livres com o número do ponto
acrescido de 1;
(c) Colocar os novos pontos (vizinhos) na la
4. Se a la estiver vazia e não se tiver atingido o ponto de destino,
não existe percurso. Caso contrário, escrever o percurso em cout.
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 30
15
18. Determinação do percurso
Ponto *percurso = new Ponto [comprimento_percurso];
// refazer o percurso do ponto final para o inicial
corrente = fim;
for (int j = comprimento_percurso -1; j = 0; j--) {
percurso[j] = corrente;
// encontrar predecessor
for (int i = 0; i n_viz; i++) {
vizinho.coluna = corrente.coluna + delta_coluna[i];
vizinho.linha = corrente.linha + delta_linha[i];
if (grelha[vizinho.linha][vizinho.coluna] == j + 2)
break;
}
corrente = vizinho;
}
FEUP/LEEC,AED,2001/2002, v0.1 Pilhas e Filas # 35
18