Você está na página 1de 10

Tipos abstratos de dados - Pilha (Stack)

Entenda o que é e como implementar a estrutura de dados pilha

Atualizado em 2017/07/05 15:59


Ativar fundo escuro

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.

As operações básicas de uma pilha são Push e Pop (empilhar e desempilhar). A ordem


com que os itens são removidos/desempilhados da pilha é inversa a ordem com que são
empilhados.

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>

using namespace std;

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 ()
{
}

void Push (int item)


{
}

int Pop ()
{
}

int Size ()
{
}

bool isEmpty ()
{
}

bool isFull ()
{
}

int Peek ()
{
}

void toString ()
{
}
};

int main (int argc, char* argv[])


{
Stack stack;

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>

using namespace std;

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;
}
}
};

int main (int argc, char* argv[])


{
Stack stack;
int item;

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;
}

cout << "Stack Pop: " << stack.Pop() << endl;


cout << "Stack Pop: " << stack.Pop() << endl;

stack.toString();

if(!stack.isFull()) {
cout << "Stack not full!" << endl;
item = 50; cout << "Stack Push: " << item << endl;
stack.Push(item);
}

stack.toString();

cout << "Stack Peek: " << stack.Peek() << endl;


stack.toString();

cout << "Stack Pop: " << stack.Pop() << endl;


cout << "Stack Pop: " << stack.Pop() << endl;
cout << "Stack Pop: " << stack.Pop() << endl;
cout << "Stack Pop: " << stack.Pop() << endl;
cout << "Stack Pop: " << stack.Pop() << endl;

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:

# Compile o código gerando como saída o binário 'stack'

$> g++ stack.cc -o stack -std=c++11

# Execute o binário executável

$> ./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 not empty!

Stack size: 5

Stack Pop: 44

Stack Pop: 76

[23]

[10]

[42]

Stack not full!

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

Stack Pop: Stack underflow

-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.

As Pilhas são normalmente utilizadas em algoritmos Backtracking, em linguagens de


programação na parte de gerência de memória, especificamente para funções (call
stack) além de em alguns compiladores para fazer parsing de sintaxe. 

Você também pode gostar