Professional Documents
Culture Documents
2. Desenhe a sequência de empilhamento de caixas (exemplo visto em aula) mostrando cada fase dos
seguintes segmentos de código (S é uma pilha de caracteres e x, y, z são variáveis).
4. Usando o método print definido em exercício anterior, execute as sequências de comandos do exercício
2, colocando entre cada comando uma chamada ao método print. Confira com a representação (desenho)
que você fez.
5. Considerando a representação contígua de uma pilha, a implementação vista em aula inicia a inserção de
elementos a partir do elemento de índice um no vetor, desperdiçando um item (índice zero). Implemente
os métodos de manipulação de pilhas em C++ considerando que:
a) os elementos são armazenados a partir do elemento de índice zero;
b) os elementos são armazenados a partir do final do vetor (assim, a pilha cresce do final do vetor em
direção ao início).
Houve alteração na complexidade dos algoritmos em relação à implementação vista em aula?
6. Como visto em aula, duas possíveis formas de implementação de uma ADT Pilha são: estática (contígua)
e dinâmica (encadeada).
a) Cite uma diferença entre uma ADT Pilha estática e dinâmica.
b) Cite uma vantagem e uma desvantagem das implementações estática e dinâmica.
8. Elabore os mesmos métodos solicitados no exercício 3 (toString, print, get e change) para pilhas
encadeadas.
1
empty e pop. Altere também a implementação para incluir um campo que armazena a quantidade de
elementos existentes na pilha.
10. Em uma pilha encadeada, o método full sempre retorna false. Por quê?
Uma cadeia típica neste conjunto pode ser especificada como wcwr, onde w contém a sequência de a’s e
b’s e wr é o reverso de w. Por exemplo, se w = ’ab’, então wr = ’ba’. Dada uma cadeia x de entrada,
formular um programa C++ que usa uma pilha para determinar se x pertence ou não ao conjunto de
cadeias, como descrito por wcwr.
12. Implemente e teste o procedimento reverseRead visto em aula usando uma pilha contígua.
13. Altere o programa reverseRead do exercício anterior para utilizar a pilha encadeada. Houve alguma
alteração no programa? Qual?
14. Escreva um programa C++ usando pilhas que leia uma linha e verifique se todos os parênteses, colchetes
e chaves encontrados estão apropriadamente em pares, ou seja, para cada (, [ e { deve haver um ), ] e }
do mesmo tipo corretamente aninhado. Se uma construção do tipo (... {... )... } é encontrada ou se um dos
símbolos não possui seu par correspondente, o programa deve escrever uma mensagem de erro,
indicando a posição na linha onde foi encontrada a falha.
15. Seja S uma pilha de inteiros e x um inteiro. Use os métodos push, pop, empty e full para escrever
procedimentos que realizem as seguintes tarefas (declare variáveis e uma segunda pilha se seus
procedimentos precisarem):
a) Faça x ser o novo topo da pilha S e deixe o antigo topo de S sem alteração.
b) Coloque x como o terceiro elemento a partir do topo de S, desde que S possua ao menos 3 inteiros.
Se não, atribua 9999 a x e deixe S inalterada.
c) Coloque x como o último elemento da pilha (fundo) ou atribua 9999 a x se S estiver vazia, deixando-
a inalterada. Dica: use uma segunda pilha.
d) Remova todas as ocorrências de x em S, deixando os demais elementos de S na mesma ordem.
16. Uma importante função teórica, conhecida como função de Ackermann é definida como:
+1 se = 0
, = − 1,1 se = 0
− 1, , −1 caso contrário
17. Em alguns casos, um programa requer o uso de duas pilhas contendo o mesmo tipo de dados. Se duas
pilhas são armazenadas em arrays separados, então uma pilha pode estourar enquanto a outra possui
espaço não utilizado disponível. Uma maneira de evitar esse problema consiste em colocar as duas pilhas
num único array, sendo que uma pilha cresce da direita para a esquerda e a outra pilha cresce da
esquerda para a direita, uma em direção à outra. Assim, se uma pilha cresce demais e a outra não, as duas
caberão no array e não haverá estouro até que todo o espaço realmente seja usado. Declare um novo tipo
DoubleStack que inclui um array e dois índices topA e topB e escreva as operações neste tipo de pilha
bem como pushA, pushB, popA e popB para manipular duas pilhas dentro de uma DoubleStack.
...
→ TopA TopB ←
2
18. Considere uma rede de desvio ferroviário:
1, 2, ..., n
Carros ferroviários numerados 1, 2, 3, ..., n estão do lado direito. Cada carro é trazido dentro da pilha e
removido a qualquer momento. Por exemplo, se n = 3, pode-se mover para dentro o primeiro, depois o
segundo e depois o terceiro. Então, em seguida, pode-se retirar os carros produzindo a nova sequência 3,
2, 1. Quais são as possíveis permutações, para n=3 e 4 que pode-se obter para os carros? Existem
permutações que não sejam possíveis? Tente encontrar uma fórmula para o número de movimentos
necessários para inverter a ordem de n carros.
3
Resolução de Alguns Exercícios
2. Desenhe a sequência de empilhamento de caixas (exemplo visto em aula) mostrando cada fase dos
seguintes segmentos de código (S é uma pilha de caracteres e x, y, z são variáveis).
2. b)
4
2. c)
2. d)
#include <sstream>
string Stack::toString()
// pre: Nenhuma
// pos: Retorna Stack no formato de string,
// Por exemplo, Stack contendo elementos 4,10,8
// retorna [4,10,8]
{ int i;
stringstream ss;
ss << "[";
for(i = 1; i <= top; i++)
{ ss << entry[i];
if( i != top )
ss << ",";
}
ss << "]";
return ss.str();
}
b. print que imprime todo o conteúdo da pilha (dica: você pode ativar o método anterior para
isso);
#include <iostream>
string Stack::print()
// pre: Nenhuma
// pos: Imprime Stack no formato de string
{ cout << toString() << endl;
}
5
c. get para obter seu i-ésimo elemento sem eliminá-lo;
#include <cstdlib>
StackEntry Stack::get(int i)
// pre: 1 <= i <= top
// pos: Retorna i-ésimo elemento da Stack sem modificá-la
{ if (i < 1 || i > top)
{ cout << "Indice invalido" << endl;
abort();
}
return entry[i];
}
d. change para alterar o valor do i-ésimo elemento contido na pilha para o valor contido na
variável x;
#include <cstdlib>
bool Stack::change(int i, StackEntry x)
// pre: 1 <= i <= top
// pos: Se for possível alterar i-ésimo elemento da Stack
// pelo valor x, retorna true; caso contrário, false.
{ if (i < 1 || i > top)
return false;
entry[i] = x;
return true;
}
4. (sem resolução)
5. Considerando a representação contígua de uma pilha, a implementação vista em aula inicia a inserção de
elementos a partir do elemento de índice um no vetor, desperdiçando um item (índice zero). Implemente
os métodos de manipulação de pilhas em C++ considerando que:
a. os elementos são armazenados a partir do elemento de índice zero;
//arquivo Stack.h
Class Stack{
public:
//….
private:
//…
StackEntry entry[MaxStack]; //remover o +1
};
//arquivo Stack.cpp
bool Stack::empty(){
return (top == -1);
}
void Stack::clear(){
top = -1;
}
int Stack::size(){
return top+1;
}
//as demais implementações continuam iguais.
b. os elementos são armazenados a partir do final do vetor (assim, a pilha cresce do final do
vetor em direção ao início).
//arquivo Stack.cpp
Stack::Stack(){ //método construtor
top = MaxStack+1;
}
6
bool Stack::empty(){
return (top == MaxStack+1);
}
bool Stack::full(){
return (top == 0);
}
void Stack::push(int x){
if (full()){
cout << "Pilha cheia";
abort();
}
top--;
entry[top] = x;
}
void Stack::pop(int &x){
if (empty()){
cout << "Pilha vazia" << endl;
abort();
}
x = entry[top];
top++;
}
void Stack::clear(){
top = MaxStack+1;
}
int Stack::size(){
return MaxStack+1 - top;
}
Houve alteração na complexidade dos algoritmos em relação à implementação vista em aula? Não
6. Como visto em aula, duas possíveis formas de implementação de uma ADT Pilha são: estática (contígua)
e dinâmica (encadeada).
a. Cite uma diferença entre uma ADT Pilha estática e dinâmica.
Op1: Estática possui tamanho fixo, dinâmica possui tamanho variável de acordo com a
necessidade.
Op2: Estática armazena os elementos de forma sequencial (contíguo) no espaço alocado de
memória, dinâmica cada elemento pode estar distante do seu próximo (não contíguo).
7. Utilizamos em aula os métodos Stack() e ~Stack(), responda as questões abaixo sobre eles:
a. O que é o método Stack() e quando é utilizado?
Stack() é um método construtor, é utilizado assim que uma nova instância de um objeto é
criada, podendo inicializar e atribuir valores aos itens da classe, alocar memória dinâmica,
etc.
b. O que é o método ~Stack() e quando é utilizado?
~Stack() é um método destrutor, é utilizado para finalizar uma pilha (já criada), excluindo
seus itens e liberando a memória utilizada por eles (em uma pilha dinâmica).
No geral, o destrutor é chamado quando um objeto sai de escopo, por ex. uma função
termina, o programa termina, um bloco que contenha variáveis locais termina, uma operação
de remoção é chamada...
c. O método ~Stack() não precisa ser implementado para uma pilha contígua. Por quê?
Uma vez que um vetor é criado, é alocado o espaço necessário para todas as suas posições.
Esse espaço só é liberado quando o vetor deixa de existir. Com isso, mesmo que uma pilha
7
use apenas uma parte do vetor, não há como “quebrar” o vetor e liberar (desalocar da
memória) o espaço não utilizado pela pilha. O espaço, do vetor, não sendo utilizado pela
pilha fica disponível para ter seu conteúdo sobrescrito quando for necessário.
8. (sem resolução)
9. (sem resolução)
10. Em uma pilha encadeada, o método full sempre retorna false. Por quê?
Porque não é declarado uma quantidade máxima de elementos da pilha, enquanto houver memória
disponível pode-se inserir um novo elemento na pilha.
11. (sem resolução)
12. (sem resolução)
13. (sem resolução)
14. Escreva um programa C++ usando pilhas que leia uma linha e verifique se todos os parênteses, colchetes
e chaves encontrados estão apropriadamente em pares, ou seja, para cada (, [ e { deve haver um ), ] e }
do mesmo tipo corretamente aninhado. Se uma construção do tipo (... {... )... } é encontrada ou se um dos
símbolos não possui seu par correspondente, o programa deve escrever uma mensagem de erro,
indicando a posição na linha onde foi encontrada a falha.
Primeiramente crie um projeto com o nome “ValidateExpression”, em seguida – dentro do projeto – crie
os arquivos abaixo:
//file validateExpression.h
//#include <cstring>
#include <string>
using namespace std;
#ifndef VALIDATEEXPRESSION_H
#define VALIDATEEXPRESSION_H
class Stack{
public:
Stack();
bool empty();
bool full();
void push(StackEntry x);
void pop(StackEntry &x);
private:
static const int MaxStack = 5;
int top; //topo da pilha
StackEntry entry[MaxStack+1]; //vetor com elementos
};
#endif
//file validateExpression.cpp
#include <iostream>
#include <string>
#include "validateexpression.h"
bool Stack::full(){
return (top == MaxStack);
}
8
cout << "Full stack. If needed, increase 'MaxStack' value..." <<
endl;
cout << "Aborting...";
abort();
}
top++;
entry[top] = x;
}
bool Stack::empty(){
return (top == 0);
}
//file d1validateExpression.cpp
#include <iostream>
#include <string>
#include "validateexpression.h"
using namespace std;
9
}
}
return 0;
}
Obs: Descomente as linhas que contêm ‘cout’ para exibir a validação de cada
caracter da expressão.
...
→ Top Top ←
A B
Primeiramente crie um projeto com o nome “DoubleStack”, em seguida – dentro do projeto – crie os
arquivos abaixo:
//file DoubleStack.h
#ifndef DOUBLESTACK_H
#define DOUBLESTACK_H
class DoubleStack{
public:
DoubleStack();
bool emptyA();
bool emptyB();
bool full();
void pushA(StackEntry x);
void pushB(StackEntry x);
void popA(StackEntry &x);
void popB(StackEntry &x);
private:
static const int MaxStack = 10;
int topA, topB; //topos das pilhas
StackEntry entry[MaxStack+1]; //vetor com elementos.. 'entry' é o
nome do vetor
};
#endif
//file DoubleStack.cpp
10
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <conio.h>
#include "DoubleStack.h"
DoubleStack::DoubleStack(){
topA = 0;
topB = MaxStack+1;
}
bool DoubleStack::emptyA(){
return (topA == 0);
}
bool DoubleStack::emptyB(){
return (topB == MaxStack+1);
}
bool DoubleStack::full(){
return (topA + 1 == topB);
}
//file d1doubleStack.cpp
#include <iostream>
11
#include <string>
#include "doubleStack.h"
using namespace std;
int menu(){
int option;
int main(){
DoubleStack stk;
int option;
int number;
12
18. Considere uma rede de desvio ferroviário:
Carros ferroviários numerados 1, 2, 3, ..., n estão do lado direito. Cada carro é trazido dentro da pilha e
removido a qualquer momento. Por exemplo, se n = 3, pode-se mover para dentro o primeiro, depois o
segundo e depois o terceiro. Então, em seguida, pode-se retirar os carros produzindo a nova sequência 3,
2, 1.
a) Quais são as possíveis permutações, para n=3 e n=4 que pode-se obter para os carros?
n=3 {{1,2,3}, {1,3,2}, {2,1,3}, {2,3,1}, {3,1,2}, {3,2,1}}
n=4 {1,2,3,4}, {1,3,2,4}, {1,4,3,2}, {2,1,3,4}, {2,1,4,3}, {2,3,1,4}, {2,3,4,1}, {2,4,3,1}, {3,2,1,4},
{3,2,4,1}, {3,4,2,1}, {4,3,2,1}}
b) Existem permutações que não sejam possíveis?
Sim, {3,1,4,2}, {4,1,2,3}, etc...
c) Tente encontrar uma fórmula para o número de movimentos necessários para inverter a ordem
de n carros.
2n
13