Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ ESTRUTURAS DE INFORMAÇÃO Já sabemos que os tipos de dados básicos ( tais como inteiro, caracter,...) são implicitamente tratados pelas linguagens de programação assim como o tipo array, string, registo. Este últimos já são colecções que guardam dados e fornecem operações de acesso que permitem juntar, retirar ou actualizar dados. O estudo de colecções de dados e formas como se organizam com as respectivas operações de acesso serão o objectivo principal da disciplina. Podemos classificar as colecções de dados em duas grandes categorias: LINEARES e NÃO LINEARES Uma estrutura de informação será do tipo linear se contiver listas de elementos em que existe uma relação de ordem entre eles, isto é, podemos dizer o 1º elemento, o 2º elemento, o 3º elemento,... Um array é um exemplo elementar de uma estrutura linear, neste caso é o índice que reflecte a ordem do elemento. No caso de estrutura não linear, os elementos não mantem este tipo de ordem entre si, existem outras relações, tal como é evidenciado po exemplo pela estrutura que representa o organigrama de uma empresa. Neste caso particular verifica-se uma relação hierárquica entre eles, mas conforme os casos outros tipo de relacionamento poderão existir. As figuras abaixo esquematizam os diferentes tipos de estruturas LINEARES e NÃO LINEARES que consideraremos ao longo da disciplina. LINEARES Indexadas Acesso Directo Acesso Sequencial Tabela Fichei Fila Array Registo Lista Pilha Fila Hash ro Prioridade HHhh ash hash HhHas _____________________________________________________________________________________________________ Departamento de h Diccio nário Engª Informática do ISEP 1 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ NÃO LINEARES Hierárquico Árvore Grupo Heap Grafo Conjunto ESTRUTURAS LINEARES (Revisão) Estes conceitos dentro das estruturas lineares já foram vistos em anos anteriores, por isso aconselha-se uma breve revisão. ARRAY È uma colecção de elementos todos do mesmo tipo que são directamente acedidos através de um índice inteiro Um array estático contem um número fixo de elementos e é alocado durante o tempo de compilação. Um array dinâmico é criado usando técnicas de alocação e gestão dinâmica de memória e pode ser redimensionado. Esta estrutura pode ser usada para guardar uma lista. No caso de uma lista sequencial, um array permite uma eficiente adição de elementos no fim da lista. É no entanto menos eficiente quando se retira um elemento, dado que a maioria das vezes temos necessidade de deslocar os elementos. Esta mesma deslocação também se verifica se o array guarda uma lista ordenada e se vamos inserir novos elementos. REGISTO _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 2 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ É uma estrutura básica que permite guardar colecções de dados de diferentes tipos. O seu uso aconselha-se quando um objecto tem diferentes atributos, por exemplo, um bilhete de avião inclui o número do voo, o número do lugar, o nome do passageiro, a agência de viagens,..., isto é, contem campos de diferentes tipos. O registo agrupa os campos mantendo um acesso directo aos dados dos campos individuais. LISTA Lista linear é a colecção de dados mais geral que guarda os elementos numa ordem sequencial.. Pode conter um número qualquer de elementos expandindo-se ou contraindo-se conforme o elementos são juntos ou retirados à lista. A limitação desta estrutura reside quando pretendemos aceder a um determinado elemento, teremos que percorrer a lista desde o início até esse elemento já que não permite acesso directo. A implementação desta estrutura poderá ser feita através de um array, mas neste caso a implementação fica limitada ao dimensionamento do array ou através de lista ligada em que as estruturas são criadas dinamicamente. Seguidamente faremos uma implementação através de lista ligada, usando a linguagem C++, com uma abordagem orientada para objecto e recorrendo ao uso de "templates" e de classes e funções "friend". Podemos ainda falar de lista ordenada, em que os seus elementos se dispõem na lista atendendo a um dado critério de ordenação (numérico, alfabético,...).Neste caso a pesquisa de um elemento pode fazer-se recorrendo ao algoritmo de pesquisa binária, cuja ordem de complexidade temporal é muito mais favorável (log2 n ) que a da pesquisa sequencial, mas só é possível se a implementação for feita através de array. PILHA ("STACK") Esta estrutura é um tipo especial de lista com acesso restrito aos seus elementos. Na pilha os elementos são colocados e retirados por um único lado da lista, referenciado por topo (ex: rima de pratos para um jantar,..) As operações de inserir e remover elementos da pilha são designadas por "push" e "pop" respectivamente. Este tipo de estrutura tem uma ordenação "LIFO- last in first out". Sempre que um elemento é adicionado ou retirado da pilha o topo é alterado. Usam-se pilhas, por exemplo, na avaliação de expressões numéricas, na recursividade, em que percorremos os elementos e a seguir acedemo-los por ordem LIFO. Os compiladores passam parâmetros às funções usando a pilha e usam-na também para guardar os valores das variáveis locais. FILA ("QUEUE") _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 3 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ Esta estrutura, tal como a pilha , é uma versão especial de lista, cujo acesso aos seus elementos é feito através do início da lista para retirar elementos e do fim da lista para adicionar elementos( ex: fila para o autocarro,...). Usando ambos os lados da lista, os elementos permanecem na fila na mesma ordem em que chegaram, obtem-se uma ordenação do tipo "FIFO - first in, first out". A fila é uma colecção útil para manter listas de espera . Em computação são utilizadas em estudos de simulação, no escalonamento de impressões de tarefas num sistema operativo. FILA DE PRIORIDADE Nalgumas aplicações altera-se a estrutura da fila dando prioridades aos seus elementos. Quando se retira um elemento desta estrutura é seleccionado aquele que tem maior prioridade (ex: chegada à urgência de um hospital,..). Tal como a estrutura anterior também é usada em sistemas operativos para escalonamento de "jobs", os de maior prioridade correm primeiro. FICHEIRO Ficheiro é uma colecção externa de dados que tem associada uma estrutura de dados designada por "stream". Acesso directo relaciona-se com ficheiros em disco, mas ficheiros em "tape" têm acesso sequencial. A operação de leitura retira dados de um "stream" de entrada e a operação de escrita junta novos dados ao fim de um "stream" de saída. Os ficheiros são muitas vezes usados para guardar grandes quantidades de dados . Por exemplo, na compilação de um programa, grandes tabelas que são criadas são muitas vezes guardadas em ficheiros temporários. TABELA DE HASH Este tipo de estrutura guarda a informação associada a uma chave. A chave é transformada num índice inteiro que é usado para aceder aos dados. Num dos métodos mais frequentes de tabela de hash, o valor inteiro é um índice de um array de informações. Depois de transformar a chave num índice, os dados associados são acedidos. A chave não precisa de ser um inteiro, pode ser um string,... terá que haver uma função que transforme essa chave num inteiro (função de hash). DICIONÁRIOS A palavra dicionário significa um conjunto de palavras e a respectiva definição, usase a palavra como uma chave. No caso de estruturas de dados, o dicionário consiste num conjunto de pares chave-valor chamados associações. Neste tipo de associação o valor é directamente acedido usando a chave como um índice. Resultado: um _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 4 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ dicionário é semelhante a um array excepto que os índices não têm que ser valores inteiros. Os dicionários são muitas chamados arrays associativos. _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 5 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ LISTA LIGADA DEFINIÇÃO EM C++ Temos que fazer a declaração forward da classe Lista uma vez que na classe No está declarada como sendo uma classe friend de No, aparecendo referenciada antes de ter sido definida (estão definidas no mesmo ficheiro). Cosiderando Lista como "friend" de No, os métodos da classe lista têm acesso aos membros privados de No. No Lista cabeça *No info prox *T ? *No template<class T> class Lista; // declaração forward class No { friend class Lista<T>; private: T * info; No<T> * prox; public: No() { info=NULL; prox=NULL; } ~No() { delete info; } }; template<class T> class Lista { private: No<T> *cabeca; _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 6 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ public: Lista<T>(); //construtor Lista<T>(const Lista<T>& l); //construtor cópia ~Lista<T>(); //destrutor bool vazia() const; Lista<T> & insere(int k,const T & elem); //junta o elemento elem na //posição imediatamente a seguir a K e devolve a nova lista //devolve o nº de elementos da lista //devolve a posição na lista onde se //encontra o elemento x Lista<T> & elimina(int k,T & x); //remove o elemento que se encontra na //posição K,traz em x o respectivo valor e devolve a nova //lista bool encontra(int k,T & x) const; //devolve true se encontra a posiçao k e traz em x o valor do elemento nessa posiçao void escreve(ostream & ostr) const; //lista os elementos da lista Lista<T> operator=(const Lista<T> & l); //operador de atribuição int comprimento() const; int pesquisa(const T & x) const; }; Construtores //Construtor sem parâmetros template <class T> Lista<T>::Lista() { cabeca = NULL; } //Construtor Cópia template <class T> Lista<T>::Lista<T>(const Lista<T>& l) { No<T> * apno =new No<T>; if(l.cabeca) cabeca=apno; else cabeca=NULL; No<T> * apnoaux=l.cabeca; while(apnoaux !=NULL) { apno->info=new T(*(apnoaux->info)); if(apnoaux->prox ==NULL) { apno->prox=NULL; apnoaux=apnoaux->prox; } else { apno->prox=new No<T>; apno=apno->prox; _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 7 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ apnoaux=apnoaux->prox; } } } Destrutor template <class T> Lista<T>::~Lista() { No<T> * temp; while(cabeca) { temp=cabeca->prox; delete cabeca; cabeca=temp; } cabeca=NULL; } Outra forma que podemos ter para o destrutor será à custa de outro método que elimine todos os elementos da lista -- destroilista() template <class T> Lista<T>::~Lista() { destroilista(); cabeca=NULL; } template <class T> void Lista<T>::destroilista() { No<T> * temp; while(cabeca) { temp=cabeca->prox; delete cabeca; cabeca=temp; } } _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 8 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ Juntar elementos à lista A inserção do elemento elem, é feita imediatamente a seguir à posição k template<class T> Lista<T> & Lista<T>::insere(int k,const T & elem) { if (k<0) cout<<"ERRO --- Posiçao incorrecta"<<'\n'; else { No<T> *temp=cabeca; for(int i=1;i<k && temp;i++) temp=temp->prox; if(k>0 && !temp) { cout<<"ERRO --- Posiçao incorrecta"<<'\n'; } else { No<T>* apno= new No<T>; apno->info= new T(elem); if(k) { apno->prox=temp->prox; temp->prox=apno; } else { apno->prox=cabeca; cabeca=apno; } } } return *this; } Operador de atribuição template <class T> Lista<T> Lista<T>::operator=(const Lista<T> & l) { if(this==& l) return *this; destroilista(); No<T> * apno =new No<T>; if(l.cabeca) cabeca=apno; _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 9 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ else cabeca=NULL; No<T> * apnoaux=l.cabeca; while(apnoaux !=NULL) { apno->info= new T(*(apnoaux->info)); if(apnoaux->prox ==NULL) { apno->prox=NULL; apnoaux=apnoaux->prox; } else { apno->prox=new No<T>; apno=apno->prox; apnoaux=apnoaux->prox; } } return *this; } //Método booleano que determina se a lista está ou não vazia. template<class T> bool Lista<T>::vazia() const { return (cabeca==NULL); } //Método que devolve o números de elementos que constituem a lista template<class T> int Lista<T>::comprimento() const { No<T> *temp; int comp=0; temp =cabeca; while (temp) { comp++; temp=temp->prox; } return comp; } Eliminar elemento //Este método retira da lista o nó da posição k, traz em x o valor do elemento nessa //posição e devolve a lista alterada. template<class T> Lista<T> & Lista<T>::elimina(int k,T & x) { _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 10 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ if (k<1 || !cabeca) cout<<"ERRO --- Posiçao incorrecta"<<endl; else { No<T> * apt=cabeca; No<T> * anterior=NULL; int i=1; while (apt && i<k) { anterior=apt; apt=apt->prox; i++; } if (i==k) { if(anterior) anterior->prox=apt->prox; //elimina do meio ou fim else cabeca=cabeca->prox; //elimina do inicio x= *(apt->info); delete apt; } else cout<<"ERRO --- Posiçao incorrecta"<<endl; } return *this; } Pesquisar //Método booleano, que devolve true se encontra a posição k, traz em x o valor do //elemento (info da classe T) nessa posição. Devolverá false caso não exista a posição //k. template<class T> bool Lista<T>::encontra(int k,T & x) const { if (k>0) { No<T> *temp=cabeca; for(int i=1;i<k && temp;i++) temp=temp->prox; if(temp !=NULL) { x= *(temp->info); // haverá que implementar o operador == na classe a //que T for instanciada return true; } } _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 11 Estruturas Lineares Estruturas de Informação _____________________________________________________________________________________________________ cout<<"ERRO --- Posiçao incorrecta"<<endl; return false; } // Este método devolve a posição na lista onde se encontra o elemento x, devolverá 0 //caso esse elemento não exista na lista. template<class T> int Lista<T>::pesquisa(const T & x)const { No<T> * apt=cabeca; int enc=0,i=0; while (apt && !enc) { if(* (apt->info)==x) // haverá que implementar o operador == na classe a que T //for instanciada enc=1; else apt=apt->prox; i++; } if (enc) return i; else return 0; } Sobrecarga do operador << O método escreve é invocado na função template que faz a sobrecarga do operador <<. Esta função template, será definida fora de qualquer classe e o método escreve deverá a parecer em todas as classes que pretendemos visualizar os seus atributos. template <class T> ostream & operator<<(ostream & ostr,const T & x) { x.escreve(ostr); return ostr; } template<class T> void Lista<T>::escreve(ostream & ostr) const { No<T>* temp=cabeca; if (cabeca) do { ostr<<*(temp->info); temp=temp->prox; }while (temp); else cout << "Lista Vazia"; cout << endl; } _____________________________________________________________________________________________________ Departamento de Engª Informática do ISEP 12