O objetivo desse artigo é que o leitor entenda o uso das pilhas. Para explicar o algoritmo, utilizaremos uma lista simplesmente encadeada. Assim, a compreensão sobre as listas encadeadas também é necessária.
Os requisitos são os tipos de dados, as estruturas, o uso do typedef, os ponteiros, as funções de usuário, as listas simplesmente encadeadas e as listas duplamente encadeadas.
A pilha é uma estrutura de dados que armazena os dados na ordem LIFO (Last In First Out - Último a Entrar, Primeiro a Sair). A recuperação dos dados é feita na ordem inversa da sua inserção.
Para o nosso exemplo, escolhemos uma lista simplesmente encadeada, apresentada na vertical. Já que a adição é sempre feita no início da lista, o primeiro elemento da lista será o último digitado e ficará no topo da pilha.
Não será utlizado um ponteiro fim, como no caso das listas simplesmente encadeadas, já que o objetivo aqui é gerar uma pilha. O que nos interessa é que o último elemento inserido será o primeiro recuperado:
Para definir um elemento da pilha, será usado o tipo struct. O elemento da pilha conterá um campo dado e um ponteiro segunte. Ele deve ser da mesma classe do elemento, caso contrário ele não poderá apontar para o elemento. O ponteiro 'seguinte' permitirá o acesso ao próximo elemento:
typedef struct ElementoLista {
char *dado;
struct ElementoLista *seguinte;
}Elemento;
Para permitir as operações na pilha, vamos salvar alguns elementos: o primeiro elemento e o número de elementos
O primeiro elemento, que se encontra no topo da pilha, nos ajudará a realizar a operação de recuperação dos dados situados no topo da pilha. Para tanto, outra estrutura diferente será utilizada, mas isso não é obrigatório, as variáveis podem ser utilizadas. Veja a composição:
typedef struct ListaDetectada{
Elemento *início;
int tamanho;
} Pilha;
O ponteiro início tem o endereço do primeiro elemento da lista enquanto a variável tamanho contém o número de elementos.
Não usaremos um ponteiro fim, pois ele não é necessário, já que vamos trabalhar unicamente no início da lista.
Seja qual for a posição na lista, o ponteiro início sempre apontará para o primeiro elemento, que estará no topo da pilha. O campo tamanho abrigará o número de elementos da pilha, independentemente da operação efetuada na pilha.
Modelo da função:
void inicialização (Pilha *monte);
Esta operação deve ser feita antes de qualquer outra da pilha. Ela inicializa o ponteiro início com o ponteiro NULL e o tamanho com o valor 0.
Função:
void inicialização (Pilha *monte){
monte->início = NULL;
monte->tamanho = 0;
}
A seguir, veremos o algoritmo de inserção e o registro dos elementos: declaração de elementos a ser inseridos, alocação da memória para o elemento seguinte, preencher o conteúdo do campo de dados, atualizar o ponteiro início para o primeiro elemento (topo da pilha) e atualizar o tamanho da pilha.
Modelo da função:
int empilhar (Pilha *monte, char *dado);
.
A primeira imagem mostra o início da inserção, portanto, a lista tem o tamanho 1 depois da inserção. A característica da pilha não está bem realçada com um único elemento, já que é o único a ser recuperado:
Por outro lado, na segunda imagem podemos observar o comportamento da pilha, embora não devemos esquecer que a integração é sempre feita no topo da pilha (no início da lista):
Função:
/* empilhar (adicionar) um elemento da pilha */
int empilhar (Pilha * monte, char *dado){
Elemento *novo_elemento;
if ((novo_elemento = (Elemento *) malloc (sizeof (Elemento))) == NULL)
return -1;
if ((novo_elemento->dado = (char *) malloc (50 * sizeof (char)))
== NULL)
return -1;
strcpy (novo_elemento->dado, dado);
novo_elemento->próximo = monte->início;
monte-> início = novo_elemento;
monte->tamanho++;
}
Para excluir o elemento da pilha, basta excluir o elemento para o qual aponta o ponteiro início. Essa operação não permite recuperar o dado no topo da pilha, mas apenas removê-lo.
Modelo da função:
int desempilhar (Pilha *monte);
A função retorna -1 em caso de falha, caso contrário, retorna 0.
Etapas:
O ponteiro remov_elemento conterá o endereço do primeiro elemento. O ponteiro início apontará para o segundo elemento (após a remoção do primeiro elemento, o segundo passará para o topo da pilha). O tamanho da pilha se reduz em um elemento:
Função:
int desempilhar (Pilha * monte){
Elemento *remov_elemento;
if (monte->tamanho == 0)
return -1;
remov_elemento = monte->início;
tas-> início = tas->início->próximo;
free (remov_elemento->dado);
free (remov_elemento);
monte ->tamanho--;
return 0;
}
Para exibir a pilha inteira, é preciso se posicionar no início da pilha (você poderá fazer isso com o ponteiro início). Em seguida, usando o ponteiro 'seguinte' de cada elemento, a pilha será percorrida do primeiro até o último elemento. A condição de parada é dada pelo tamanho da pilha.
Função:
/* visualização da pilha */
void exibe (Pilha * monte){
Elemento *atual;
int i;
atual = monte->início;
for(i=0;i<tas->tamanho;++i){
printf("\t\t%s\n", corrente->dado);
atual = atual->seguinte;
}
}
Para recuperar o dado no topo da pilha sem removê-lo, usamos uma macro. A macro faz a leitura dos dados no topo da pilha usando o ponteiro início:
#define pilha_dado(monte) monte->início->dado
/*********************\
/***********************\
/*********************\
Foto: © Everypixel.