UFABC Universidade Federal do ABC Prof. Andr G. R. Balan andre.balan@ufabc.edu.br Aula 03 Pilhas Sequencial Encadeada Aplicaes Contedo 2 Estruturas de dados Para explicar esse conceito, partimos do princpio que todo algoritmo manipula dados (entrada e sada) Uma estrutura de dados consiste em um meio de se armazenar dados com o objetivo de facilitar ao algoritmo: Receber novos dados Encontrar dados que ele precise em um determinado momento Se livrar de dados no mais relevantes Introduo 3 Estruturas de dados Uma estrutura de dados deve especificar: Uma tcnica para armazenas os dados (sequencial, encadeado hierrquico, enfileirado, empilhado) Operaes que podem ser realizada sobre o conjunto de dados As bsicas so: Insero, Remoo, Busca Estas mesmas operaes bsicas so aplicadas grande maioria das estruturas, porm elas funcionam de maneira diferente. Exemplo: qual a diferena entre enfileirar e empilhar? Introduo 4 Estruturas de dados Uma estrutura de dados pode ser projetada para armazenar dados em diversas meios de armazenamento: Disco Fita Memria principal (interna) Nesta disciplina estudaremos em primeiro plano as estruturas de dados para memria principal Introduo 5 Estruturas de dados elementares (lineares) Pilhas Filas Listas Mtodos de implementao Sequencial Encadeado Introduo 6 Uma pilha sequencial uma ED implementada em cima de um vetor de dados. Os elementos da pilha ocuparo posies contguas de memria, por isso o termo senquencial. As duas principais operaes so Empilha / Push Desempilha / Pop Essas duas operaes operam somente em uma posio da pilha: o topo Pilha sequencial (sequential stack) 7 O ltimo elemento empilhado sempre o primeiro a ser desempilhado Por isso, a pilha tambm chamada de estrutura LIFO (Last In, First Out) Pilha sequencial 8 Vamos supor uma pilha sequencial de inteiros Inicialmente, alocamos um vetor como desejarmos: alocao esttica (stack) ou dinmica (heap) Vamos preferir alocao dinmica Pilha sequencial 0 1 n-1 . . . 9 Inicialmente a pilha est vazia Precisamos de uma varivel ndice para determinar o topo da pilha Pilha sequencial 0 1 n-1 . . . 10 Inicialmente a pilha est vazia Precisamos de uma varivel ndice para determinar o topo da pilha Como a pilha est vazia, topo indica (aponta) para uma posio antes do comeo do vetor: topo = -1; Pilha sequencial 0 1 n-1 . . . topo 11 Vamos inserir o inteiro 20 Pilha sequencial 0 1 n-1 . . . topo 12 Primeiro incrementamos de uma unidade o topo: topo++ Pilha sequencial 0 1 n-1 . . . topo 13 Primeiro incrementamos de uma unidade o topo: topo++ Em seguida, inserimos o elemento 20, onde topo est indicando Pilha sequencial 0 1 n-1 . . . topo 20 14 Agora vamos empilhar outros elementos Pilha sequencial 0 1 n-1 . . . topo 20 15 Agora vamos empilhar outros elementos Pilha sequencial 0 1 n-1 . . . topo 20 30 16 Agora vamos empilhar outros elementos Pilha sequencial 0 1 n-1 . . . topo 20 30 40 17 Mas preciso ter cuidado: o vetor tem uma quantidade limitada de memria Um nova insero requer sempre uma verificao inicial: topo == n-1 ? Se sim, a pilha deve avisar o utilizar por meio de algum mecanismo que a pilha est cheia Pilha sequencial 0 1 n-1 . . . topo 20 30 40 50 60 70 80 90 100 18 Mecanismos de comunicao ED/utilizador: Mensagem direta: cout << Pilha cheia Valor de retorno: return(false); Mecanismo de excees: throw logic_error("Pilha cheia"); As excees so a melhor forma de se comunicar algo ao utilizador da classe. Vamos procurar sempre utilizar excees Pilha sequencial 19 Agora vamos desempilhar alguns elementos Pilha sequencial 0 1 n-1 . . . topo 20 30 40 50 60 70 80 90 100 20 Agora vamos desempilhar alguns elementos Pilha sequencial 0 1 n-1 . . . topo 20 30 40 50 60 70 80 90 100 21 Agora vamos desempilhar alguns elementos Pilha sequencial 0 1 n-1 . . . topo 20 30 40 50 60 70 80 90 22 Agora vamos desempilhar alguns elementos Tambm preciso tomar cuidado: Se a pilha estiver vazia, uma tentativa de desempilhamento deve gerar um aviso ao utilizador: Pilha vazia Pilha sequencial 0 1 n-1 . . . topo 20 23 Agora, vamos criar uma classe PilhaSeq para inteiros, em C++ Pilha sequencial class PilhaSeq { public: PilhaSeq(int size); ~PilhaSeq(); // Mtodos principais void empilha(int elem); int desempilha(); // Mtodos auxiliares bool cheia(); bool vazia(); int tamanho(); private: int *vetor; int topo, size; }; P i l h a S e q . h 24 Acontece, que queremos um ED genrica. Vamos utilizar templates Pilha sequencial template <typename Tipo> class PilhaSeq { public: PilhaSeq(int size); ~PilhaSeq(); // Mtodos principais void empilha(const Tipo &elem); Tipo desempilha(); // Mtodos auxiliares bool cheia(); bool vazia(); int tamanho(); private: Tipo *vetor; int topo, size; }; P i l h a S e q . h 25 Vamos convensionar que a ED utilizar excees para comunicao Pilha sequencial template <typename Tipo> class PilhaSeq { public: PilhaSeq(int size); ~PilhaSeq(); // Mtodos principais void empilha(const Tipo &elem) throw (logic_error); Tipo desempilha() throw (logic_error); // Mtodos auxiliares bool cheia(); bool vazia(); int tamanho(); private: Tipo *vetor; int topo, size; }; P i l h a S e q . h 26 Implementaes Pilha sequencial // Construtor template <typename Tipo> PilhaSeq<Tipo>::PilhaSeq(int size) { this->size = size; vetor = new Tipo[size]; topo = -1; } P i l h a S e q . h 27 Implementaes Pilha sequencial // Destrutor template <typename Tipo> PilhaSeq<Tipo>::~PilhaSeq() { delete [] vetor; } P i l h a S e q . h 28 Implementaes Pilha sequencial // Empilha template <typename Tipo> void PilhaSeq<Tipo>::empilha(const Tipo &el) throw (logic_error){ if (topo == (size-1)) throw logic_error("Pilha cheia"); vetor[++topo] = el; } P i l h a S e q . h 29 Implementaes Pilha sequencial // Desempilha template <typename Tipo> Tipo PilhaSeq<Tipo>::desempilha() throw (logic_error) { if (topo < 0) throw logic_error("Pilha vazia"); return(vetor[topo--]); } P i l h a S e q . h 30 Implementaes Pilha sequencial // Mtodos auxiliares template <typename Tipo> bool PilhaSeq<Tipo>::cheia() { return(topo == (size-1)); } template <typename Tipo> bool PilhaSeq<Tipo>::vazia() { return(topo <0); } template <typename Tipo> int PilhaSeq<Tipo>::tamanho() { return(topo + 1); } P i l h a S e q . h 31 Demais informaes Pilha sequencial #ifndef _PilhaSeq_H #define _PilhaSeq_H #include <stdexcept.h> using std::logic_error; namespace ED { template <typename Tipo> class PilhaSeq { ... }; // Implementaes } #endif P i l h a S e q . h Inclusion Guards Biblioteca para excees Para utilizar a classe logic_error Para organizao 32 Exemplo de utilizao da nossa PilhaSeq Pilha sequencial #include <iostream> #include PilhaSeq.h using ED::PilhaSeq; int main(int argc, char** argv) { PilhaSeq<int> p(30); for (int i=0; i<20; i++) p.empilha(i); for (int i=0; i<20; i++) cout << p.desempilha() << endl; } M a i n . c p p 33 Exemplo de utilizao da nossa PilhaSeq Pilha sequencial #include <iostream> #include PilhaSeq.h using ED::PilhaSeq; int main(int argc, char** argv) { PilhaSeq<int> p(30); for (int i=0; i<20; i++) p.empilha(i); for (int i=0; i<20; i++) cout << p.desempilha() << endl; } M a i n . c p p Aqui, o utilizador no est interessado em receber mensagens da pilha. Tudo bem, ele no obrigado, mas se ele quiser, ele pode: try...catch 34 Recebendo mensagens da pilha com try... catch Pilha sequencial #include <iostream> #include PilhaSeq.h using ED::PilhaSeq; int main(int argc, char** argv) { PilhaSeq<int> p(30); try { for (int i=0; i<20; i++) p.empilha(i); for (int i=0; i<20; i++) cout << p.desempilha() << endl; } catch (logic_error err){ cout << err.what(); } } M a i n . c p p 35 Exemplo de uma pilha de pontos (x,y) Pilha sequencial 36 Exemplo de uma pilha de pontos (x,y) #include <iostream> #include PilhaSeq.h using ED::PilhaSeq; struct Ponto2D { int x, y; }; int main(int argc, char** argv) { PilhaSeq<Ponto2D> p(50); Ponto2D pt; for (int i=0; i<5; i++) for (int j=0; j<5; j++) { pt.x = i; pt.y = j; p.empilha(pt); } for (int i=0; i<25; i++) { pt = p.desempilha(); cout << pt.x << , << pt.y << endl; } } M a i n . c p p 37 Perguntas sobre o exemplo anterior Onde est alocada a varivel pt ? Onde est alocado o objeto p? Onde est alocada a memria sequencial para a pilha do objeto p? Quantos bytes utilizado na memria sequencial para a pilha do objeto p? O que acontece se fizermos Ponto2D p1, p2; p1.x=10; p2 = p1; No lugar de estruct poderamos ter uma classe? Pilha sequencial 38 Qual a maior desvantagem da pilha sequencial? Pilha sequencial 39 Qual a maior desvantagem da pilha sequencial? Ela tem um tamanho fixo que, definido na sua criao Se a pilha est com poucos elementos, muita memria estar sendo alocada desnecessariamente Se a pilha est quase cheia, ento, provavelmente, ela ir encher em breve, e no poderemos mais inserir elementos Pilha sequencial 40 Podemos dar um bom palpite sobre o tamanho inicial da ED, mas sempre estaremos entre um ou outro caso: Memria sub utilizada Falta de espao Pilha sequencial 41 Por isso, podemos utilizar uma tcnica clssica de implementao de EDs lineares: o encadeamento; No encadeamento, utilizamos ns, ou, clulas, para armazenar a informao. Ento, por meio de ponteiros, mantemos um encadeamento das clulas. Pilha encadeada 42 Exemplo de pilha encadeada: Pilha encadeada topo 43 Pelo fato de a ED ter agora um tamanho no fixo, comum chamar a ED de pilha dinmica. Mas no confunda esse nome dinmica, com o tipo de alocao de memria. Vimos que a fila sequencial tambm utiliza alocao dinmica de memria, embora com um tamanho fixo, pr-estabelecido. Pilha encadeada topo 44 O n da ED encadeada pode ser uma struct ou uma classe. O importante que ela tenha uma parte de dados e um ponteiro para outra clula do mesmo tipo Pilha encadeada class Celula public: Tipo el; Celula *prox; }; struct Celula Tipo el; Celula *prox; }; ou Celula *prox; Tipo el; 45 Estado inicial da pilha Pilha encadeada topo 46 Empilhando um elemento: 10 Pilha encadeada topo Celula *nova = new Celula; nova 47 Empilhando um elemento: 10 Pilha encadeada topo 10 nova 48 Celula *nova = new Celula; nova->el = 10; Empilhando um elemento: 10 Pilha encadeada topo 10 nova 49 Celula *nova = new Celula; nova->el = 10; nova->prox = topo; Empilhando um elemento: 10 Pilha encadeada topo 10 nova 50 Celula *nova = new Celula; nova->el = 10; nova->prox = topo; topo = nova; nelem++; Empilhando um elemento: 20 Pilha encadeada 10 nova topo 20 51 Celula *nova = new Celula; nova->el = 20; nova->prox = topo; topo = nova; nelem++; Desempilhando Pilha encadeada Tipo ret = topo->el; 10 topo 20 ret 20 52 Desempilhando Pilha encadeada Tipo ret = topo->el; Celula *aux = topo; 10 topo 20 aux 20 53 ret Desempilhando Pilha encadeada Tipo ret = topo->el; Celula *aux = topo; topo = topo->prox; 10 topo 20 aux 20 54 ret Desempilhando Pilha encadeada Tipo ret = topo->el; Celula *aux = topo; topo = topo->prox; delete aux; nelem--; return(ret); 10 topo 20 55 ret Desempilhando Pilha encadeada Tipo ret = topo->el; Celula *aux = topo; topo = topo->prox; delete aux; nelem--; return(ret); topo 10 56 ret Classe PilhaEnc em C++. Pilha encadeada template <typename Tipo> class PilhaEnc { public: PilhaEnc(); ~PilhaEnc(); // Mtodos principais void empilha(const Tipo &elem) throw (logic_error); Tipo desempilha() throw (logic_error); // Mtodos auxiliares bool vazia(); int tamanho(); private: struct Celula { Tipo el; Celula *prox; }; Celula *topo; int nelem; }; P i l h a E n c . h 57 Implementaes Pilha encadeada // Construtor template <typename Tipo> PilhaEnc<Tipo>::PilhaEnc() { topo = NULL; nelem = 0; } P i l h a E n c . h 58 Implementaes Pilha encadeada // Destrutor template <typename Tipo> PilhaEnc<Tipo>::~PilhaEnc() { Celula *aux; while (topo) { aux = topo; topo = topo->prox; delete aux; } } P i l h a E n c . h 59 Implementaes Pilha encadeada // Empilha template <typename Tipo> void PilhaEnc<Tipo>::empilha(const Tipo &el) throw (logic_error){ Celula *nova = new Celula; if (nova == NULL) throw logic_error("Falta memoria\n"); nova->el = el; // Cria-se uma cpia do elemento el nova->prox = topo; topo = nova; nelem++; } P i l h a E n c . h 60 Implementaes Pilha encadeada // Desempilha template <typename Tipo> Tipo PilhaEnc<Tipo>::desempilha() throw (logic_error) { if (nelem == 0) throw logic_error("Pilha vazia\n"); Tipo ret = topo->el; Celula *aux = topo; topo = topo->prox; delete aux; nelem--; return(ret); } P i l h a E n c . h 61 Implementaes Pilha encadeada // Mtodos auxiliares template <typename Tipo> bool PilhaEnc<Tipo>::vazia() { return(nelem == 0); } template <typename Tipo> int PilhaEnc<Tipo>::tamanho() { return(nelem); } P i l h a E n c . h 62 A pilha encadeada possui a vantagem de no ter um tamanho fixo. Alm disso, a pilha encadeada facilita o aproveitamento de memria interna, pois mais fcil alocar muitos blocos pequenos de memria do que um nico bloco muito grande. Porm, o encadeamento utiliza mais memria pois precisa de memria para os ponteiros. Pilha encadeada x sequencial 63 A pilha sequencial tende a realizar empilhamento e desempilhamento mais rpido, pois no precisa alocar e desalocar memria no momento dessas operaes. Em linha gerais, no podemos dizer que uma melhor que a outra. Temos que levar em considerao cada aplicao separadamente. Pilha encadeada x sequencial 64 Nas implementaes apresentadas nestas aulas, os itens de dados so armazenados dentro da memria alocada para a pilha, seja ela sequencial ou encadeada. Existe tambm a possibilidade de uma implementao onde no so armazenados os dados em si, mas sim, ponteiros para esses itens. O grande problema desse tipo de implementao fica por conta da desalocao de memria. Pilha encadeada x sequencial 65 Implementar: pilha sequencial pilha encadeada Utilizar os dois tipos de pilha para desenvolver as seguintes aplicaes : Exerccios 66 Exerccios 1 Implementar uma calculadora para expresses aritmticas de notao ps-fixada Exemplos 3*(4+5) <-> 3 4 5 + * (3+4)*(7+8) <-> 3 4 + 7 8 + * Algoritmo bsico Leia um nmero ou um operador Se for nmero, empilhe Se for operador, desempilhe os ltimos 2 nmeros, realize a operao correspondente e empilhe o resultado 67 Exerccios 2 Implementar a funo paint bucket 68 . . . . x . . x x . . . x . . x x . . . . . . . . Entrada: uma matriz mxn com x ou . em cada elemento. Exemplo: Um ponto semente. Ex (2, 3) e Sada: uma matriz alterada. Exemplo: . . . . x . . x x x . . x x x x x x x x x x x x x Utilizar hard-coding Exerccios 2 Implementar a funo paint bucket Algoritmo bsico empilhe o ponto inicial (x i , y i ) enquanto a pilha no estiver vazia desempilha um ponto (x, y) se (x,y) um ponto vlido e se no estiver pintado (x) pinte (x, y) e empilhe os 4 vizinhos 69 Exerccios 3 Implementar um programa para validar uma expresso aritmtica com relao ao balenceamento de () [] e {} Exemplo: d^{[(a+b) *c] * [c*(a+d) ^(d+e)]} ok d^{[(a+b) *c] * c*(a+d) ^(d+e)]} errado 70 Exerccios 3 Implementar um programa para validar uma expresso aritmtica com relao ao balenceamento de () [] e {} Algoritmo Ler um smbolo (letra, operador, um delimitador ) Se for um ( ou um [ ou um { Empilha o smbolo Se for um ) ou um ] ou um } Desempilha um smbolo e compara com o smbolo de entrada Se forem diferente, retorna errado Se for igual, continua. Ao trmino da cadeia, se a pilha estiver vazia, retorna correto 71