Tipos Abstratos de Dados em C++
Tipos Abstratos de Dados em C++
Tipos Abstratos de Dados em C++
A pilha ou stack é uma estrutura de dados LIFO (Last in First out), ou seja o último a
entrar é o primeiro a sair. Uma analogia seria uma pilha de pratos. O último prato
empilhado deve ser o primeiro a ser removido da pilha.
Uma pilha deve guardar em uma variável seu tamanho máximo. Quando esse tamanho é
excedido dizemos que ocorreu um estouro da pilha (Stack overflow). Quando tentamos
remover um elemento de uma pilha vazia temos o erro inverso, um (Stack underflow).
Na imagem demonstramos uma pilha (Stack) onde temos um limite de altura da pilha,
uma variável com o topo e as operações de Push e Pop. Vamos começar a implementar
uma classe em C++ para construir essa estrutura de dados:
#include <iostream>
class Stack {
private:
static const int STACK_MAX_SIZE = 5; // Tamanho máximo da
pilha
int top; // variável que controla qual o topo atual da pilha
int items[STACK_MAX_SIZE]; // Pilha a ser inseridos dados
public:
Stack ()
{
}
int Pop ()
{
}
int Size ()
{
}
bool isEmpty ()
{
}
bool isFull ()
{
}
int Peek ()
{
}
void toString ()
{
}
};
return EXIT_SUCCESS;
}
Temos nesse exemplo o esqueleto de uma classe que implementa a estrutura de dados
Pilha (stack). Nosso tamanho máximo de pilha é 10. Os valores permitidos na pilha são
do tipo int. A nossa função main somente cria um objeto da classe Stack e sai do
programa. Vamos começar a implementar os métodos de nossa Pilha. A primeira coisa a
se fazer é criar o método construtor da classe.
/*
* Inicializa as variáveis
*/
Stack ()
top = -1;
No construtor inicializamos o topo da pilha, que inicialmente está vazia, como sendo -1.
O próximo método será o Push, que empilha um item na Pilha. Esse método, segundo a
notação O (Big O notation) tem complexidade O(1) pois a inserção é apenas uma
atribuição. Caso a variável top seja maior ou igual ao tamanho máximo de nossa Pilha
imprimimos uma mensagem de Stack overflow.
/*
* Empilha um item na Pilha
*/
void Push (int item)
{
if(top >= STACK_MAX_SIZE - 1) {
cout << "Stack overflow" << endl;
} else {
items[++top] = item;
}
}
Feita a implementação que empilha itens, vamos fazer a que desempilha itens. A
operação Pop também tem complexidade O(1), pois apenas retorna o valor de um índice
da Pilha. Caso o topo da Pilha indique que ela está vazia, ele imprime a mensagem de
erro Stack underflow e retorna o valor -1.
/*
* Desempilha o último item da lista
*/
int Pop ()
{
if(top <= -1) {
cout << "Stack underflow" << endl;
return -1;
} else {
return items[top--];
}
}
Os principais métodos já foram implementados Push e Pop. Para termos uma estrutura
de Pilha completa iremos fazer alguns métodos que irão nos ajudar. O próximo método
é o Size que retorna o atual tamanho da Pilha.
/*
* Retorna o atual tamanho da Pilha
*/
int Size ()
{
return top + 1;
}
Outros métodos que nos ajudam são os que verificam se a pilha está vazia e cheia. Eles
retorna um booleano (true ou false).
/*
* Retorna true caso a pilha esteja vazia. false, caso contrário
*/
bool isEmpty ()
{
return (top == -1) ? true : false;
}
/*
* Retorna true caso a pilha esteja cheia. false, caso contrário
*/
bool isFull ()
{
return (top == STACK_MAX_SIZE - 1) ? true : false;
}
Vamos criar agora o método Peek que é muito parecido com o Pop. A principal
diferença é que ele não desempilha o item, ou seja, não decrementa o topo da Pilha.
/*
* Pega o item do topo da Pilha mas não o desempilha
*/
int Peek ()
{
return (top == -1) ? -1 : items[top];
}
Para fins de debugging vamos criar o método toString que imprime nossa Pilha na tela.
Ele percorre nossa Pilha do topo até a base imprimindo seus valores linha a linha.
/*
* Imprime a pilha
*/
void toString ()
{
for(int i=top; i > -1; i--) {
cout << "[" << items[i] << "]" << endl;
}
}
Agora que implementamos todos os métodos necessários da nossa estrutura de dados
Pilha, vamos dar uma olhada na classe completa. Nesse exemplo abaixo já colocamos
uma pequena função main que faz uso de nossa classe Stack.
#include <iostream>
class Stack {
private:
static const int STACK_MAX_SIZE = 5; // Tamanho máximo da
pilha
int top; // variável que controla qual o topo atual da pilha
int items[STACK_MAX_SIZE]; // Pilha a ser inseridos dados
public:
/*
* Inicializa as variáveis
*/
Stack ()
{
top = -1;
}
/*
* Empilha um item na Pilha
*/
void Push (int item)
{
if(top >= STACK_MAX_SIZE - 1) {
cout << "Stack overflow" << endl;
} else {
items[++top] = item;
}
}
/*
* Desempilha o último item da lista
*/
int Pop ()
{
if(top <= -1) {
cout << "Stack underflow" << endl;
return -1;
} else {
return items[top--];
}
}
/*
* Retorna o atual tamanho da Pilha
*/
int Size ()
{
return top + 1;
}
/*
* Retorna true caso a pilha esteja vazia. false, caso
contrário
*/
bool isEmpty ()
{
return (top == -1) ? true : false;
}
/*
* Retorna true caso a pilha esteja cheia. false, caso
contrário
*/
bool isFull ()
{
return (top == STACK_MAX_SIZE - 1) ? true : false;
}
/*
* Pega o item do topo da Pilha mas não o desempilha
*/
int Peek ()
{
return (top == -1) ? -1 : items[top];
}
/*
* Imprime a pilha
*/
void toString ()
{
for(int i=top; i > -1; i--) {
cout << "[" << items[i] << "]" << endl;
}
}
};
item = 42; cout << "Stack Push: " << item << endl;
stack.Push(item);
item = 10; cout << "Stack Push: " << item << endl;
stack.Push(item);
item = 23; cout << "Stack Push: " << item << endl;
stack.Push(item);
item = 76; cout << "Stack Push: " << item << endl;
stack.Push(item);
item = 44; cout << "Stack Push: " << item << endl;
stack.Push(item);
item = 87; cout << "Stack Push: " << item << endl;
stack.Push(item);
stack.toString();
if(!stack.isEmpty()) {
cout << "Stack not empty!" << endl;
cout << "Stack size: " << stack.Size() << endl;
}
stack.toString();
if(!stack.isFull()) {
cout << "Stack not full!" << endl;
item = 50; cout << "Stack Push: " << item << endl;
stack.Push(item);
}
stack.toString();
return EXIT_SUCCESS;
}
Agora temos nossa classe completa implementando uma Pilha. Para ver esse tipo
abstrato de dados na prática, copie esse último código e cole em um arquivo chamado
stack.cc. Para compilar o programa iremos utilizar GNU gcc g++. Assim, para rodar
esse arquivo execute os comandos a seguir e veja a seguinte saída no terminal:
$> ./stack
Stack Push: 42
Stack Push: 10
Stack Push: 23
Stack Push: 76
Stack Push: 44
Stack Push: 87
Stack overflow
[44]
[76]
[23]
[10]
[42]
Stack size: 5
Stack Pop: 44
Stack Pop: 76
[23]
[10]
[42]
Stack Push: 50
[50]
[23]
[10]
[42]
Stack Peek: 50
[50]
[23]
[10]
[42]
Stack Pop: 50
Stack Pop: 23
Stack Pop: 10
Stack Pop: 42
-1
Vimos então a estrutura de dados Pilha (Stack). Essa estrutura de dados segue o padrão
LIFO e possui operações eficientes. As pilhas com tamanho máximo fixo, tem a
limitação de não poder crescer de maneira dinâmica. Para tal, poderíamos criar a
estrutura de Pilha utilizando listas encadeadas.