LISTAS Dilvan Moreira, parcialmente baseado em material do prof. Ricardo Campello Lista Linear Estrutura de representação de informação em que os elementos são mantidos de forma linear, ou seja, dispostos um após o outro Ex. listas de nomes, de valores, de pessoas, etc. Pode ser ordenada ou não Estruturação de informação em listas ocorre em vários domínios P. ex. lista telefônica Lista Linear Operação Básica em Listas: Busca por um elemento através de uma “chave” Ex. busca na lista telefônica: dado um nome, buscamos o telefone Inserção e Remoção de elementos: Implementação manipulação depende da organização da lista distinta de listas ordenadas e não ordenadas TAD para Lista Inicializar lista (p. ex. vazia): void Inserir elemento: void Definir(Lista *L); Inserir(tipo_elem e, Lista *L); Localizar a posição (na lista) de um elemento dado: int Localizar(tipo_elem e, Lista *L); retorna a posição do elemento na lista retorna um valor inválido caso ele não esteja na lista TAD para Lista Acessar elemento em posição dada: tipo_elem Buscar(intp, Lista *L); retorna o elemento da lista na posição fornecida sem alterá-la se posição não existir, retorna um valor inválido Eliminar elemento na posição dada: int Remover(intp, Lista *L); Retorna 1 (true) se bem sucedida Retorna 0 (false) se posição dada é inválida Obter número de elementos na lista: int Tamanho(Lista *L); TAD para Lista Apagar a lista: void Apagar(Lista *L); Destruir a lista: void Destruir(Lista *L); lista não é mais acessível (só para implementações dinâmicas) Obter posição seguinte à do último elemento: int Fim(Lista *L); Verificar se lista está vazia, verificar se está cheia... ... EDs Estáticas para Listas Realizações Estáticas (vetores): Seqüencial: seqüência de elementos disponíveis de forma consecutiva Encadeada: seqüência de elementos disponíveis de forma não consecutiva EDs Dinâmicas para Listas Realizações Dinâmicas (ponteiros e memória): Simplesmente Encadeada: Encadeamento Duplamente Encadeada: Encadeamento de ponteiros unidirecional de ponteiros bidirecional Cada realização possui vantagens e desvantagens Lista Estática Sequencial Implementação a Seguir: Exemplo de Lista Estática Seqüencial em C Projetada para Lista Não Ordenada Adaptação para lista ordenada como exercício no final Lista Estática Sequencial #define #define #define #define MAX 100 TRUE 1 FALSE 0 bool int /* Max. Tamanho da Lista */ typedef struct{ Tipo_1 chave; Tipo_2 info; } elem; /* P. ex. int chave; */ /* P. ex. char info[50] */ /* Tipo do Elemento */ typedef struct{ int nelem; elem A[MAX+1]; } Lista; /* Tipo da Lista */ Lista L; /* Exemplo de Declaração*/ Lista Estática Sequencial void Definir(Lista *L){ /* O(1) */ /* Define uma lista vazia */ L->nelem= 0; L->A[0].chave = 0; /* Célula não utilizada */ L->A[0].info[0] = '\0'; /* Célula não utilizada */ } int Tamanho(Lista *L){ /* O(1) */ /* Retorna o tamanho da Lista (0 caso vazia) */ return L->nelem; } Lista Estática Sequencial int Fim(Lista *L){ /* O(1) */ /* Retorna a posição após o último elemento da lista */ return (L->nelem + 1); } bool Lista_vazia(Lista *L){ /* O(1) */ /* Retorna true se lista vazia, false caso contrário */ return (L->nelem == 0); } bool Lista_cheia(Lista *L){ /* O(1) */ /* Retorna true se lista cheia, false caso contrário */ return (L->nelem == MAX); } Lista Estática Sequencial bool Inserir(elem* x, int p, Lista *L){ /* Insere elemento x na posição p da Lista L. Se a lista é tal que L=a1,a2,...,ap,...,an então L=a1,a2,...,ap-1,x,ap+1,...,an. Se p=Fim(L) então L=a1,a2,...,an,x. Devolve true se sucesso, false c.c. (L cheia ou não tem posição p). L não ordenada! */ int atual; if (Lista_cheia(L)) return FALSE; /* lista cheia */ else if (p > Fim(L) || p < 1) return FALSE; /* posição não existe */ else { for(atual = L->nelem; atual >= p; atual--) L->A[atual+1] = L->A[atual]; L->A[p]= *x; L->nelem++; return TRUE; /* inserção com sucesso */ } } /* O(L.nelem) no pior caso. Qual o pior caso? */ Lista Estática Sequencial int Localizar(elem* x, Lista *L){ /* Retorna a posição de x na Lista. Se x ocorre mais de uma vez, retorna posição da 1a ocorrência. Se x não ocorre, retorna 0 */ int atual = 1; if (!Lista_vazia(L)){ while(atual <= L->nelem){ if(igual(&A[atual], x)) /* iguais ?*/ return atual; /* retorno sucesso */ else atual++; } } return 0; /* retorno em insucesso ou lista vazia */ } /* O(L.nelem) no pior caso. Qual o pior caso? */ Lista Estática Sequencial bool igual(elem* a, elem* b) { /* Testa igualdade entre elementos por algum critério particular */ } return (a->chave==b->chave); /* O(1) */ elem* Buscar(int p, Lista *L){ /* Retorna elem. da posição p, ou elem. inválido se p é inválida */ } if (p >= Fim(L) || p < 1 || Lista_vazia(L)) return x; /* retorna elemento inválido */ else return &(L->A[p]); /* O(1) */ Lista Estática Sequencial bool Remover(int p, Lista *L){ /* Remove o elemento da posição p da Lista. Se L = a1,a2,...,an então tem-se a1,a2,...,ap-1,ap+1,...,an. Devolve true se sucesso, false c.c. (L não tem posição p, inclusive se p = Fim(L)) */ FALSE; int atual; if(p >= Fim(L) || p < 1 || Lista_vazia(L)) return else { atual++) for (atual = p+1; atual <= L->nelem; L->A[atual-1] = L->A[atual]; L->nelem--; } return TRUE; } /* O(L.nelem) no pior caso. Qual o pior caso? */ Lista Estática Sequencial em C++ Como ficaria essa implementação em C++? Seria muito diferente? Lista Estática Sequencial Orig. #define #define #define #define MAX 100 TRUE 1 FALSE 0 bool int typedef struct{ char info[50]; } elem; /* Max. Tamanho da Lista */ /* P. ex. char info[50] */ /* Tipo do Elemento */ typedef struct{ int nelem; tipo_elem A[MAX+1]; } Lista; /* Tipo da Lista */ Lista L; /* Exemplo de Declaração*/ Lista Estática Sequencial em C++ #define #define #define #define MAX 100 TRUE 1 FALSE 0 bool int /* Max. Tamanho da Lista */ class Elem { char info[50]; /* P. ex. char info[50] */ … } /* Tipo do Elemento */ class List { int nelem; tipo_elem A[MAX+1]; … } /* Tipo da Lista */ Lista Estática Sequencial Orig. void Definir(Lista *L){ /* O(1) */ /* Define uma lista vazia */ L->nelem= 0; L->A[0].info[0] = '\0'; /* Célula não utilizada */ } int Tamanho(Lista *L){ /* O(1) */ /* Retorna o tamanho da Lista (0 caso vazia) */ return L->nelem; } Lista Estática Sequencial em C++ void Lista::Lista(){ /* O(1) */ /* Define uma lista vazia */ nelem= 0; A[0].info[0] = '\0'; /* Célula não utilizada */ } int Lista::Tamanho(){ /* O(1) */ /* Retorna o tamanho da Lista (0 caso vazia) */ return nelem; } Lista Estática Sequencial Orig. int Fim(Lista *L){ /* O(1) */ /* Retorna a posição após o último elemento da lista */ return (L->nelem + 1); } bool Lista_vazia(Lista *L){ /* O(1) */ /* Retorna true se lista vazia, false caso contrário */ return (L->nelem == 0); } bool Lista_cheia(Lista *L){ /* O(1) */ /* Retorna true se lista cheia, false caso contrário */ return (L->nelem == MAX); } Lista Estática Sequencial C++ int List::Fim(){ /* O(1) */ /* Retorna a posição após o último elemento da lista */ return (nelem + 1); } bool List::Lista_vazia(){ /* O(1) */ /* Retorna true se lista vazia, false caso contrário */ return (nelem == 0); } bool List::Lista_cheia(){ /* O(1) */ /* Retorna true se lista cheia, false caso contrário */ return (nelem == MAX); } Lista Estática Sequencial Orig. bool Inserir(elem* x, int p, Lista *L){ /* Insere elemento x na posição p da Lista L. Se a lista é tal que L=a1,a2,...,ap,...,an então L=a1,a2,...,ap-1,x,ap+1,...,an. Se p=Fim(L) então L=a1,a2,...,an,x. Devolve true se sucesso, false c.c. (L cheia ou não tem posição p). L não ordenada! */ int atual; if (Lista_cheia(L)) return FALSE; /* lista cheia */ else if (p > Fim(L) || p < 1) return FALSE; /* posição não existe */ else { for(atual = L->nelem; atual >= p; atual--) L->A[atual+1] = L->A[atual]; L->A[p] = *x; L->nelem++; return TRUE; /* inserção com sucesso */ } } /* O(L.nelem) no pior caso. Qual o pior caso? */ Lista Estática Sequencial C++ bool List::Inserir(Elem* x, int p){ /* Insere elemento x na posição p da Lista L. Se a lista é tal que L=a1,a2,...,ap,...,an então L=a1,a2,...,ap-1,x,ap+1,...,an. Se p=Fim(L) então L=a1,a2,...,an,x. Devolve true se sucesso, false c.c. (L cheia ou não tem posição p). L não ordenada! */ int atual; if (Lista_cheia()) return FALSE; /* lista cheia */ else if (p > Fim() || p < 1) return FALSE; /* posição não existe */ else { for(atual = nelem; atual >= p; atual--) A[atual+1] = A[atual]; A[p] = *x; nelem++; return TRUE; /* inserção com sucesso */ } } /* O(L.nelem) no pior caso. Qual o pior caso? */ Lista Estática Sequencial Orig int Localizar(elem* x, Lista *L){ /* Retorna a posição de x na Lista. Se x ocorre mais de uma vez, retorna posição da 1a ocorrência. Se x não ocorre, retorna 0 */ int atual = 1; if (!Lista_vazia(L)){ while(atual <= L->nelem){ if(igual(&L->A[atual], x)) return atual; /* retorno em sucesso */ else atual++; } } return 0; /* retorno em insucesso ou lista vazia */ } /* O(L.nelem) no pior caso. Qual o pior caso? */ Lista Estática Sequencial C++ int List::Localizar(Elem* x){ /* Retorna a posição de x na Lista. Se x ocorre mais de uma vez, retorna posição da 1a ocorrência. Se x não ocorre, retorna 0 */ int atual = 1; if (!Lista_vazia()){ while(atual <= nelem){ if(A[atual].Igual(x)) return atual; /* retorno em sucesso */ else atual++; } } return 0; /* retorno em insucesso ou lista vazia */ } /* O(L.nelem) no pior caso. Qual o pior caso? */ Lista Estática Sequencial Orig bool igual(elem* a, elem* b) { /* Testa igualdade entre elementos por algum critério particular */ } return (strcmp(a->chave,b->chave)==0); /* O(1) */ elem* Buscar(int p, Lista *L){ /* Retorna elem. da posição p, ou elem. inválido se p é inválida */ } if (p >= Fim(L) || p < 1 || Lista_vazia(L)) return x; /* retorna elemento inválido */ else return L->A[p]; /* O(1) */ Lista Estática Sequencial C++ bool Elem::igual(elem* b) { /* Testa igualdade entre elementos por algum critério particular */ } return (strcmp(info,b->info)==0); /* O(1) */ Elem* List::Buscar(int p){ /* Retorna elem. da posição p, ou elem. inválido se p é inválida */ } if (p >= Fim() || p < 1 || Lista_vazia()) return null; /* retorna inválido */ else return &A[p]; /* O(1) */ Lista Estática Sequencial Orig bool Remover(int p, Lista *L){ /* Remove o elemento da posição p da Lista. Se L = a1,a2,...,an então tem-se a1,a2,...,ap-1,ap+1,...,an. Devolve true se sucesso, false c.c. (L não tem posição p, inclusive se p = Fim(L)) */ FALSE; int atual; if(p >= Fim(L) || p < 1 || Lista_vazia(L)) return else { atual++) for (atual = p+1; atual <= L->nelem; L->A[atual-1] = L->A[atual]; L->nelem--; } return TRUE; } /* O(L.nelem) no pior caso. Qual o pior caso? */ Lista Estática Sequencial C++ bool List::Remover(int p){ /* Remove o elemento da posição p da Lista. Se L = a1,a2,...,an então tem-se a1,a2,...,ap-1,ap+1,...,an. Devolve true se sucesso, false c.c. (L não tem posição p, inclusive se p = Fim(L)) */ FALSE; int atual; if(p >= Fim() || p < 1 || Lista_vazia()) return else { for (atual = p+1; atual <= nelem; atual++) A[atual-1] = A[atual]; nelem--; } return TRUE; } /* O(L.nelem) no pior caso. Qual o pior caso? */ . Checando 1. 2. 3. 4. Modifique a operação Remover do TAD Lista Estática Seqüencial para que esta retorne o elemento removido e não um valor lógico. Modifique a operação Localizar do TAD Lista Estática Seqüencial para que esta receba apenas a chave do elemento procurado e não o elemento todo. Modifique a operação Localizar do TAD Lista Estática Seqüencial para que esta retorne um ponteiro para uma cópia do elemento localizado, cópia esta alocada dinamicamente em memória. Retorne NULL se o elemento não existir na lista. Implemente o TAD Lista Estática Seqüencial em C, de forma modular, com módulos separados de interface (arquivo .h) e implementação (arquivo .c), e faça alguns testes.aula Checando Modifique a oImplemente uma nova operação de inserção para o TAD Lista Estática Seqüencial para substituir aquela que foi apresentada anteriormente nesses slides (Inserir). Essa nova operação deve inserir o novo elemento de forma a manter sempre a lista ordenadapelas chaves dos seus elementos. 5. 5. OBS: Como a lista deve ser mantida ordenada, essa operação deve determinar a posição de inserção automaticamente, não recebê-la como parâmetro. Nesse exercício, faça isso percorrendo um a um os elementos da lista a partir do início (depois vide exercício 7). Checando Considerando que a operação de inserção utilizada no TAD Lista Estática Seqüencial é aquela implementada no exercício 5, reimplemente também a operação Localizar do TAD para que esta faça uso do fato da lista estar ordenada para realizar uma busca muito mais eficiente (binária) pelo elemento desejado. 6. 6. 7. Qual o tempo de execução assintótico (BIG-O) de pior caso da operação acima ? Qual o pior caso? Justifique. Estando a nova operação de localização via busca binária do exercício 6 disponível, é possível utilizá-la para simplificar a implementação da operação de inserção ordenada do exercício 5? Explique. Bibliografia N. Ziviani, Projeto de Algoritmos, Thomson, 2a. Edição, 2004 A. M. Tenembaum et al., Data Structures Using C, Prentice-Hall, 1990 J. L. Szwarcfiter & L. Markenzon, Estruturas de Dados e seus Algoritmos, LTC, 1994 Perguntas?