Curso: Bacharelado em Ciência da Computação -
BCC
Disciplina: (1479A) Estruturas de Dados I – ED1 Professora: Simone das Graças Domingues Prado e-mail: [email protected]
home-page: wwwp.fc.unesp.br/~simonedp/ed1bcc.htm
Apostila 02 – Conceito de Pilha em ED
Objetivos:
⇒ Conceituar tipo abstrato de dados pilha ⇒ Conhecer e implementar vários tipos pilhas ⇒ Trabalhar com pilhas estáticas
⇒ Trabalhar com pilhas dinâmicas
Conteúdo:
1. Introdução 2. Pilhas estáticas
2.1. Primeira definição de Pilhas Estáticas 2.2. Segunda definição de Pilhas Estáticas
3. Pilhas usando Lista Linear Simplesmente Encadeada
4. Pilhas usando Lista Linear Simplesmente Encadeada com Descritor 5. Exercícios
1. Introdução
Vários tipos de listas já foram estudados. Percebe-se que, nas listas, o acesso era feito sem nenhuma restrição. Podia-se inserir e remover elementos em qualquer posição, só dependendo se a lista era ordenada ou não.
As Pilhas e as Filas são listas que possuem critérios de acessos restritivos. Vejamos os critérios de acesso mais usuais:
FIFO - (First Input, First Output) ou seja, o primeiro que entrar é o primeiro a sair.
LIFO - (Last Input, First Output) ou seja, o último que entrar numa lista é o primeiro a sair dela
Levando em consideração esses critérios de acesso e pelo que se conhece, informalmente, por pilha e fila pode-se identificar que uma Pilha é uma lista que tem como critério de acesso o LIFO, e a fila, o FIFO.
Dessa forma, uma Pilha é um conjunto de elementos no qual novos itens podem ser inseridos ou removidos em uma única extremidade, a qual é chamada de topo. A pilha possuirá características bem próprias de acesso e por conseqüência terá operações bem simples de empilhar e desempilhar elementos.
Veja a figura a seguir onde se têm quatro peças para empilhadas rotuladas como A, B, C e D. Para a pilha abaixo ficar com essa configuração deve-se colocar primeiro a peça A, depois a B, C e D. e se for retirar só conseguiremos tirar na ordem: D, C, B e A
A manipulação de uma pilha pode ser feita de forma estática ou dinâmica. A forma estática usará variável do tipo matriz e as pilhas dinâmicas usarão ponteiros. Numa pilha estática tem-se que preocupar com o tamanho da pilha, já que a alocação da variável é feita de forma estática. Assim, ao inserir um elemento tem-se de verificar se a pilha comporta. Na pilha dinâmica (que usa ponteiros) ao inserir o elemento tem-se de verificar se existe memória para a criação de um novo elemento na pilha.
2. Pilhas estáticas
Na implementação de pilhas estáticas pode surgir várias formas de construção do tipo de dado pilha. Veja aqui duas definições: uma definição usará um vetor e variável topo separadas e a outra definição usará uma estrutura que conterá o vetor e o topo.
Independente de qual seja a construção do tipo pilha, deve-se preocupar com as operações básicas de: • verificação de pilha vazia (se estiver vazia, não se pode tentar desempilhar elementos)
• verificação de pilha cheia (se estiver cheia, não se pode empilhar elementos) • empilhar um elemento na pilha sem ultrapassar o tamanho do vetor
• desempilhar um elemento da pilha se a pilha contiver elementos • imprimir a pilha
2.1. Primeira definição de Pilha Estática
Trabalha-se com a idéia de ter um vetor de inteiros e o topo é controlado por uma variável inteira fora desse vetor.
#define MAX 100
typedef int def_pilha[MAX];
A referência é feita da seguinte forma para:
def_pilha pilha; int topo;
Topo => topo
Elemento do topo => pilha[topo]
As Rotinas:
1. Verificação se a pilha contém ou não elementos
boolean vazia (int topo){ return (topo == -1); }
2. Verificação se a pilha já está cheia, ou seja, se não comporta mais elementos
boolean cheia (int topo){
return (topo == (MAX-1)); }
3. Empilhar um elemento, ou seja, inserir um elemento no topo da pilha
boolean empilha (def_pilha pilha, int *topo, int x){ if (cheia(*topo))return False;
pilha[++(*topo)]=x; return True;
}
4. Desempilhar elemento, ou seja, retirar um elemento do topo da pilha
boolean desempilha (def_pilha pilha, int *topo, int x){ if (vazia(*topo))return False;
*x = pilha[(*topo)--];
return True; }
Exemplo 01:
Implementar uma rotina para conversão de um número inteiro em decimal para um número binário.void converter_nro(int elemento, def_pilha binario, int* topo){ int resto;
inicializa_pilha(binário,topo); while (elemento != 0) {
resto = elemento % 2;
empilha(binario, topo, resto); elemento = elemento / 2;} }
2.2. Segunda definição de Pilha estática
Essa definição trabalha com uma estrutura que contem um vetor de elementos e uma variável inteira para guardar a posição do topo. Todas as operações são executadas em cima dessa estrutura.
#define MAX 10 typedef struct {
int topo;
int elementos[MAX];
} def_pilha;
A referência é feita da seguinte forma para:
def_pilha pilha;
Topo => pilha.topo
Elemento do topo => pilha.elementos[pilha.topo]
As Rotinas:
1. Verificação se a pilha tem elementos
boolean vazia (def_pilha pilha){ return (pilha.topo == -1); }
2. Verificação se a pilha já está cheia, ou seja, se não comporta mais elementos
boolean cheia (def_pilha pilha){ return (pilha.topo == (MAX-1)); }
3. Empilhar um elemento, ou seja, inserir um elemento no topo da pilha
boolean empilha (def_pilha *pilha, int x){ if (cheia(*pilha)) return False;
pilha->elementos [++(pilha->topo)] = x; return True;
}
4. Desempilhar elemento, ou seja, retirar um elemento do topo da pilha
boolean desempilha (def_pilha *pilha, int *x){ if (vazia(*pilha)) return False;
*x = pilha->elementos[pilha->topo--];
return True;
}
5. Imprimir a pilha sem retirar elementos
void mostra_pilha (def_pilha pilha){
int i=pilha.topo;
if (pilha.topo >= 0){ for(; i>=0; i--)
printf("\t%d",pilha.elementos[i]);} else printf(" esta vazia");
}
Exemplo 02:
Escrever uma rotina para inverter uma frase usando pilha.void converter_frase(char frase[MAX], def_pilha* pilha){
int i=0;
for (;i<strlen(frase);i++) empilha(pilha,frase[i]); }
3. Pilhas usando Lista Linear Simplesmente Encadeada
Definição:
Typedef struct no_pilha{
int info;
struct no_pilha *prox;
} *def_pilha;
As Rotinas:
1. Inicialização de uma pilha
void inicializa (def_pilha *pilha){ *pilha = NULL;
}
2. Verificação se a pilha tem elementos
boolean vazia(def_pilha pilha){
return(pilha==NULL);
}
3. Empilhar um elemento, ou seja, inserir um elemento no topo da pilha
void empilha(int numero, def_pilha *pilha){ def_pilha q = cria_no(numero);
if (!vazia(*pilha)) q->prox = *pilha; *pilha = q;
}
4. Desempilhar elemento, ou seja, retirar um elemento do topo da pilha
boolean desempilha(int *numero,def_pilha * pilha){ def_pilha q;
if(vazia(*pilha)) return False; q = (*pilha); *numero = q->info; *pilha = (*pilha)->prox; free(q); return True; }
6. Imprimir a pilha sem retirar elementos
void imprime(def_pilha pilha){ while (pilha!=NULL){
printf("\t%d", pilha->info); pilha=pilha->prox;};
printf("\n"); }
4. Pilhas usando Lista Linear Simplesmente Encadeada com
Descritor
typedef struct no_pilha{ int info;
struct no_pilha *prox;
} No;
typedef struct descritor{
int quantidade;
No *pilha;
} *def_pilha;
Da mesma forma, a inserção e remoção serão feitas no início da mesma.
As Rotinas:
1. Inicialização de uma pilha
void inicializa (def_pilha *Pilha) {
Pilha->quantidade=0; Pilha->pilha = NULL; }
2. Verificação se a pilha tem elementos
boolean vazia(def_pilha pilha) {
return(pilha.pilha==NULL);
}
3. Empilhar um elemento, ou seja, inserir um elemento no topo da pilha
void empilha(int numero, def_pilha *pilha) { No* q = cria_no(numero);
if (!vazia(*pilha)) q->prox = (*pilha).pilha; (*pilha).pilha = q;
(*pilha).quantidade++; }
4. Desempilhar elemento, ou seja, retirar um elemento do topo da pilha
boolean desempilha(int *numero, def_pilha *Pilha) { No* q;
if(vazia(*Pilha)) return False; q = Pilha->pilha; *numero = q->info; Pilha->pilha = Pilha->pilha->prox; free(q); Pilha->quantidade--; return True; }
6. Imprimir a pilha sem retirar elementos
void imprime(def_pilha pilha) { while (pilha.pilha!=NULL){
printf("\t%d", pilha.pilha->info); pilha=pilha->prox;};
printf("\n"); }
Exemplo 03:
O estacionamento Bashemin contém uma única alameda que guarda carros. Existe apenas uma entrada/saída no estacionamento, em uma extremidade da alameda. Se chegar um cliente para retirar um carro que não seja o mais próximo da saída, todos os carros bloqueando seu caminho sairão do estacionamento, e os outros carros voltarão a ocupar a seqüência inicial. Escreva um programa que processe um grupo de linhas de entrada.
O programa deve imprimir uma mensagem sempre que um carro sair.
Quando um carro sair do estacionamento, a mensagem deverá incluir o número de vezes em que o carro foi manobrado para fora do estacionamento para permitir que outros carros saíssem.
#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <string.h>
typedef enum {False,True} boolean; struct carros{
char placa[7];
int manobra;}; typedef struct no_pilha{
struct carros info;
struct no_pilha *prox;
} No; typedef struct descritor{
int quantidade;
No *pilha;
} def_pilha;
void inicializa(def_pilha *Pilha) {
Pilha->quantidade=0; Pilha->pilha=NULL; }
boolean vazia(def_pilha Pilha) {
return(Pilha.pilha==NULL);
}
No* cria_no(struct carros dados) { No* q;
q =(No*) malloc(sizeof(struct no_pilha)); q->info=dados;
q->prox=NULL; return q; }
void empilha(struct carros dados, def_pilha* Pilha)
{ No* q=cria_no(dados);
if(!vazia(*Pilha))q->prox = Pilha->pilha; Pilha->pilha = q;
Pilha->quantidade++; }
boolean desempilha(struct carros *dados, def_pilha* Pilha) { No* q;
if (vazia(*Pilha)) return False; q = Pilha->pilha; *dados = q->info; Pilha->pilha = Pilha->pilha->prox; free(q); Pilha->quantidade--; return True; }
void imprime(def_pilha pilha)
{ printf("\n\nOs carros no estacionamento: \n"); while (pilha.pilha!=NULL){ printf("%s - ",(pilha.pilha->info).placa); printf("%d\n", (pilha.pilha->info).manobra); pilha.pilha = pilha.pilha->prox;}; printf("\n"); }
boolean retira(def_pilha* pilha, struct carros* dados) { def_pilha q,p;
booelean achou = False;; struct carros carro;
q = *pilha; inicializa(&p); while (!vazia(q) && !achou){
desempilha(&carro,&q);
if (strcmp(carro.placa,dados->placa) != 0) empilha(carro,&p);
else{ achou = True;
dados->manobra = carro.manobra;}} while (desempilha(&carro,&p)){ (carro.manobra)++; empilha(carro,&q); } (*pilha) = q; return achou; }
int main (void)
{ def_pilha estacionamento; struct carros carro; int opcao; inicializa(&estacionamento); do{ do{ printf("\n(1)Entrada\n(2)Saida\n(3)Imprimir\n(4)Encerrar\nOpcao="); opcao=getche()-48;
}while (opcao<0 && opcao>4); switch(opcao){
case 1: printf("\n\nQual a placa do carro = ");
scanf("%s",&carro.placa);
carro.manobra = 0;
empilha(carro,&estacionamento);break;
case 2: if (vazia(estacionamento))
printf("\nNao tem mais carros ...\n");
else{
printf("\nQual a placa do carro = "); scanf("%s",&carro.placa);
if (retira(&estacionamento,&carro))
vezes\n",carro.placa,carro.manobra);
else printf("O carro de placa %s nao se encontra mais aqui \n",carro.placa);}
break;
case 3: if (!vazia(estacionamento)) imprime(estacionamento);
else printf("Nao tem mais carros ...\n");printf("\n\n");
break;} }while (opcao != 4);
printf("\n\n\aDigite algo para encerrar....");getch(); return 1;
}
5. Exercícios:
1) (Pilha Estática) Construir uma pilha que usa somente um vetor, onde pilha[0] contém o controle do topo da pilha e pilha[1] a pilha[MAX-1] contêm os elementos da pilha. Defina as rotinas de: a) inicialização da pilha
b) verificação de pilha vazia c) verificação de pilha cheia d) empilhar um elemento e) desempilhar um elemento f) mostrar a pilha
2) (Pilha Estática) Em algumas aplicações tem-se que trabalhar com mais de uma pilha ao mesmo tempo. Pode-se implementar essas pilhas num mesmo vetor, de forma que cada uma ocupe parte desse vetor. Faça um programa que tenha duas pilhas alocadas em um mesmo vetor, onde cada uma cresce em sentido oposto, sem que as duas fiquem totalmente ocupadas simultaneamente. Implemente as operações básicas de
a) inicialização das duas pilhas
b) verificação se as pilhas estão vazias c) verificação se as pilhas estão cheias
d) empilhar um elemento em cada uma das pilhas e) desempilhar um elemento de cada uma das pilhas f) mostrar as pilhas
3) (Pilha Estática) Um estacionamento é composto de um único beco que guarda no máximo 10 carros. Existe apenas uma entrada/saída no estacionamento, em uma extremidade do beco. Se chegar um cliente para retirar um carro que não seja o mais próximo da saída, todos os carros bloqueando seu caminho sairão do estacionamento, e os outros carros voltarão a ocupar a seqüência inicial. Escreva um programa que processe um grupo de cartões do estacionamento. O programa deve imprimir uma mensagem sempre que um carro chegar ou sair. Quando um carro chegar, a mensagem deve especificar se existe ou não vaga para o carro no estacionamento. Se não houver vaga, o carro partirá sem entrar no estacionamento.
Quando um carro sair do estacionamento, a mensagem deverá incluir o número de vezes em que o carro foi manobrado para fora e para dentro do estacionamento para permitir que outros carros saíssem.
4) (Pilha Dinâmica) Imagine um colecionador de vinhos que compra vinhos recentes e guarda-os em uma adega para envelhecerem, e que a cada ocasião especial abre sempre sua última aquisição (para poupar os mais antigos). Construa um programa que:
b) Informe qual vinho deve ser aberto em uma ocasião especial c) Relacione as cinco aquisições mais antigas
As informações básicas que o registro do vinho deve conter são: nome do produto e safra.
5) (Pilha Dinâmica) O problema das Torres de Hanói é bastante estudado em computação. O problema consiste em n discos de diferentes diâmetros e três estacas: A, B e C. Inicialmente os discos estão encaixados na estaca A onde o menor está sempre encima do maior disco. O objetivo é deslocar os discos para uma estaca C, usando a estaca B como auxiliar. Somente o primeiro disco de toda estaca pode ser deslocado. Isso para nós dá a idéia de pilhas. Portanto construa a resolução desse exercício usando pilhas e para n = 4, ou seja, para 04 discos.
6) (Pilha Dinâmica) Uma pilha pode ser usada para rastrear os tipos de escopos encontrados numa expressão matemática e verificar se o uso deles está correto. Os delimitadores de escopos podem ser os parênteses, os colchetes e as chaves. Escreva um programa que leia uma expressão matemática e verifique se os escopos estão posicionados de forma correta.
7) (Pilha Dinâmica) Considere a existência de um tipo PILHA de números de ponto flutuante. Implemente um programa que leia duas pilhas, P1 e P2, e passe todos os elementos da pilha P2 para o topo da pilha P1 sem trocar a ordem e sem ferir o conceito de pilha. A figura a seguir ilustra essa concatenação de pilhas:
8) (Pilha Dinâmica) Considere que a pilha use a estrutura de uma Lista Circular Simplesmente Encadeada. Implemente as operações básicas de:
a) inicialização da pilha
b) verificação se a pilhas está vazia c) empilhar um elemento na pilha d) desempilhar um elemento da pilha e) mostrar a pilha
9) (Pilha Dinâmica) Considere que a pilha use a estrutura de uma Lista Linear Simplesmente Encadeada com Descritor Dinâmico. Implemente as operações básicas de:
a) inicialização da pilha
b) verificação se a pilhas está vazia c) empilhar um elemento na pilha d) desempilhar um elemento da pilha e) mostrar a pilha