As listas duplamente encadeadas podem ser usadas quando várias operações de inserção e remoção de elementos são necessárias. Elas são estruturas de dados semelhantes às listas simplesmente encadeadas e sua alocação da memória é feita durante a execução. No entanto, em comparação com as listas simplesmente encadeadas, a conexão entre os elementos é feita através de dois ponteiros (um que aponta para o elemento anterior e o outro para o seguinte). O ponteiro anterior ao primeiro elemento deve apontar para NULL (o início da lista), bem como o ponteiro seguinte ao último elemento.
Para acessar um elemento, a lista pode ser percorrida pelos dois lados, partindo do alto, o ponteiro seguinte permite o deslocamento para o próximo elemento e começando do final ou do ponteiro anterior que permite o deslocamento para o elemento anterior. O movimento é feito em ambas as direções, do primeiro para o último elemento e vice-versa.
Em primeiro lugar, estabeleça um elemento da lista com o tipo struct. O elemento da lista terá um campo, um ponteiro anterior e um ponteiro seguinte.
Os ponteiros anterior e seguinte devem ser do mesmo tipo que o elemento, caso contrário eles não poderão apontar para um elemento da lista. O ponteiro do anterior permitirá o acesso ao elemento anterior, enquanto que o ponteiro seguinte vai permitir o acesso ao próximo elemento:
typedef struct dl_elementoLista { char *dado; struct dl_elementoLista *anterior; struct dl_elementoLista *seguinte; }dl_elemento;
Para o controle da lista, o melhor é salvar certos elementos: o primeiro elemento, o último elemento e o número de elementos. Para tanto, usaremos outra estrutura (isto não é obrigatório, pois as variáveis podem ser usadas). Veja a sua composição:
typedef struct dl_ListaDetetada { dl_elemento *início; dl_elemento *fim; int tamanho; }dl_Lista;
Qualquer que seja a posição na lista, os ponteiros de início e fim vão apontar, respectivamente, para o primeiro e o último elemento. O tamanho terá o número de elementos da lista, seja qual for a operação efetuada na lista.
Basta uma única função para a inserção e a exclusão se ela foi bem desenvolvida. No entanto, lembrem-se que este artigo é puramente didático: é por essa razão que eu escrevi uma função para cada operação de inserção e de exclusão.
Protótipo da função:
ver a inicialização (Lista *lista)
: deverá ser feita antes de qualquer outra operação da lista. A função inicia o ponteiro de lançamento e de fim, sempre através do ponteiro NULL e valor 0.
A função:
void inicialização (Lista *lista){ lista>início = NULL; lista>fim = NULL; tamanho = 0; }
Veja o algoritmo de inserção e de backup dos elementos:
Para adicionar um elemento na lista, existem várias soluções:
Protótipo da função:
int ins_na_lista_vazia (dl_Lista * lista, char *dado);
A função volta para -1, no caso de uma falha, caso contrário ela retorna 0.
Passos:
A função:
int inserção_na_lista_vazia (dl_Lista * lista, char *dado){ dl_elemento *novo_elemento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); novo_elemento>anterior = lista>início; novo_elemento>seguinte = lista>fim; lista>início = novo_elemento; lista>fim = novo_elemento; liste>tamanho++; return 0; }
Protótipo da função:
int ins_início_lista (dl_Lista * lista, char *dado);
Volta a função -1 com falha, ela retorna 0.
Passos:
A função:
int ins_início_lista (dl_Lista * lista, char *dado){ dl_elemento *novo_elemento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); novo_elemento>anterior = NULL; novo_elemento>seguinte = lista>início; lista>início>anterior = novo_elemento; lista>início = novo_elemento; lista>tamanho++; return 0; }
Protótipo da função:
int ins_fim_lista (dl_Lista * lista, char *dado);
A função volta para -1 no caso de falha, do contrário ela retorna 0.
Passos:
A função:
int ins_fim_lista (dl_Lista * lista, char *dado){ dl_elemento *novo_elemento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); novo_elemento>seguinte = NULL; novo_elemento>anterior = lista>fim; lista>fim>seguinte = novo_elemento; lista>fim = novo_elemento; lista>tamanho++; return 0; }
Protótipo da função:
int ins_antes (dl_Lista * lista, char *dado, int pos);
A função aparece novamente -1 se houver falha, se não for assim ela volta para o 0.
A inserção será feita antes de uma certa posição transformada em argumento para a função. A posição indicada não deve ser nem o primeiro nem o último elemento. Neste caso, é preciso utilizar as funções de inserção no início e/ou fim da lista.
Passos:
A função:
int ins_antes (dl_Lista * lista, char *dado, int pos){ int i; dl_elemento *novo_elemento, *em andamento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); em andamento = lista>início; for (i = 1; i < pos; ++i) em andamento = em andamento>seguinte; novo_elemento>seguinte = em andamento; novo_elemento> anterior = em andamento>anterior; if(em andamento>anterior == NULL) lista>início = novo_elemento; else em andamento>anterior>seguinte = novo_elemento; em andamento>anterior = novo_elemento; lista>tamanho++; return 0; }
Protótipo da função:
int ins_apres (dl_Lista * lista, char *dado, int pos);
Com isso, volta -1 no caso de falha, se não for assim ela retorna 0.
A inserção será feita após uma determinada posição transformada em argumento da função. A posição indicada não deve ser o último elemento. Neste caso, utilize a função de inserção no final da lista.
Passos:
A função:
int ins_apres (dl_Lista * lista, char *dado, int pos){ int i; dl_elemento *novo_elemento, *em andamento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); em andamento = lista>início; for (i = 1; i < pos; ++i) em andamento = em andamento>seguinte; novo_elemento>seguinte = em andamento>seguinte; novo_elemento>anterior = em andamento; if(em andamento > seguinte == NULL) lista>fim = novo_elemento; else em andamento >seguinte>anterior = novo_elemento; em andamento >seguinte = novo_elemento; lista>tamanho++; return 0; }
Veja o algoritmo de remoção de um elemento da lista:
Para remover um elemento da lista existem várias soluções:
Porém, a remoção no início e no fim das listas duplamente encadeadas, assim como antes ou depois de um elemento vem a ser a remoção da posição 0 (zero) ou da posição N (N = número de elementos da lista) ou em outro lugar da lista.
No caso das listas duplamente encadeadas, pode-se remover em qualquer posição pelos ponteiros anterior e seguinte, que mantêm a ligação entre os elementos da lista. É por isso que vamos criar uma única função se quisermos remover o elemento do início da lista, vamos escolher a posição zero e quisermos remover o elemento do fim da lista, vamos escolher a posição N (o número de el, sementos) ou se quisermos remover um elemento qualquer, então vamos escolher a sua posição na lista.
Protótipo da função:
int remov(dl_Lista *lista, int pos);
Essa função volta a exibir -1 em caso de falha ou retorna 0.
Podemos distinguir várias situações:
Passos:
A posição escolhida é 1 (o caso da remoção do primeiro elemento da lista). O ponteiro remove elemento e conterá o endereço do primeiro elemento e o ponteiro de início integrará o endereço mantido pelo ponteiro seguinte ao 1° elemento que queremos remover (se este ponteiro equivale a NULL, então atualizaremos o ponteiro de fim já que é o caso de uma lista com apenas um elemento. Caso contrário, apontaríamos o ponteiro anterior ao segundo elemento para NULL).
A posição escolhida é igual, com o número de elementos da lista e o ponteiro remov_elemento conterá o endereço do último elemento. Nós apontaremos o ponteiro seguinte ao antepenúltimo elemento (é o elemento para o qual aponta o ponteiro anterior ao último elemento), para NULL. Nós atualizaremos o ponteiro de fim.
A posição escolhida é aleatória na lista. O ponteiro remov_elemento conterá o endereço do elemento a ser removido e o ponteiro seguinte ao elemento que precede o elemento a ser removido aponta para o endereço mantido pelo ponteiro seguinte ao elemento a ser removido. O ponteiro anterior ao elemento que sucede o elemento a ser removido aponta para o endereço mantido pelo ponteiro anterior ao elemento a ser removido e o tamanho da lista vai ter menos um elemento.
A função:
int remov(dl_Lista *lista, int pos){ int i; dl_elemento *remov_elemento,*em andamento; if(lista>tamanho == 0) return -1; if(pos == 1){ /* remoção do 1° elemento */ remov_elemento = lista>início; lista>início = lista>início>seguinte; if(lista>início == NULL) lista>fim = NULL; else lista>início>anterior == NULL; }else if(pos == lista>tamanho){ /* remoção do último elemento */ remov_elemento = lista>fim; lista>fim>anterior>seguinte = NULL; lista>fim = lista>fim>anterior; }else { /* remoção em outro lugar */ em andamento = lista>início; for(i=1;i<pos;++i) em andamento = em andamento>seguinte; remov_elemento = em andamento; em andamento>anterior>seguinte = em andamento>seguinte; em andamento>seguinte>anterior = em andamento>anterior; } free(remov_elemento>dado); free(remov_elemento); lista>tamanho--; return 0; }
Nós podemos, exibindo a lista inteira, nos posicionar no início ou no fim da lista (o ponteiro de início ou de fim o permitirá). Depois, utilizando o ponteiro seguinte ou anterior de cada elemento, a lista vai ser varrida do 1° ao último elemento ou do último ao primeiro elemento. É o ponteiro seguinte do último elemento que dá a condição de parada e que equivale ao NULL no caso da leitura do início para o fim da lista, ou pelo ponteiro anterior do primeiro elemento que equivale ao NULL, no caso de uma leitura do fim para o início da lista.
As funções:
void exibe(dl_Lista *lista){ /* mostrar avançando */ dl_elemento *em andamento; em andamento = lista>início; /* ponto de partida do 1° elemento */ printf("[ "); while(em andamento != NULL){ printf("%s ",em andamento>dado); em andamento = em andamento>seguinte; } printf("]\n"); } void exibe_inv(dl_Lista *lista){ /* mostrar recuando */ dl_elemento *em andamento; em andamento = lista>fim; /* ponto de partida do último elemento */ printf("[ "); while(em andamento != NULL){ printf("%s : ",em andamento>dado); em andamento = em andamento>anterior; } printf("]\n"); }
Para destruir a lista inteira, utilizarei a remoção da posição 1 enquanto o tamanho for maior que zero.
A função:
void destruir(dl_Lista *lista){ while(lista>tamanho > 0) remov(liste,1); }
/* ---------- dlista.h ----------- */ typedef struct dl_elementoLista{ char *dado; struct dl_elementoLista *anterior; struct dl_elementoLista*seguinte; } dl_elemento; typedef struct dl_ListaDetectada{ dl_elemento *início; dl_elemento *fim; int tamanho; } dl_Lista; /* inicialização da lista */ void inicialização (dl_Lista * lista); dl_elemento *aloc (dl_elemento * novo_elemento); /* INSERÇÃO */ int ins_em_uma_lista_vazia (dl_Lista * lista, char *dado); int ins_início_lista (dl_Lista * lista, char *dado); int ins_fim_lista (dl_Lista * lista, char *dado); int ins_depois (dl_Lista * lista, char *dado, int pos); int ins_antes (dl_Lista * lista, char *dado, int pos); /* REMOÇÃO */ int REMOV(dl_Lista *lista, int pos); void exibe (dl_Lista * lista); /**************************/ void exibe_inv (dl_Lista * lista); void destruir (dl_Lista * lista); /* -------- FIM lista.h --------- */
/* ---------- dlista_function.h ----------- */ void inicialização (dl_Lista * lista){ lista>início = NULL; lista>fim = NULL; lista>tamanho = 0; } int inserção_em_uma_lista_vazia (dl_Lista * lista, char *dado){ dl_elemento *novo_elemento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); novo_elemento>anterior = NULL; novo_elemento>seguinte = NULL; lista>início = novo_elemento; lista>fim = novo_elemento; lista>tamanho++; return 0; } int ins_início_lista (dl_Lista * lista, char *dado){ dl_elemento *novo_elemento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento >dado, dado); novo_elemento >anterior = NULL; novo_elemento >seguinte = liste>início; lista>início >anterior = novo_elemento; lista>início = novo_elemento; lista>tamanho++; return 0; } int ins_fim_lista (dl_Lista * lista, char *dado){ dl_elemento *novo_elemento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); novo_elemento >seguinte = NULL; novo_elemento >anterior = lista>fim; lista>fim>seguinte = novo_elemento; lista>fim = novo_elemento; lista>tamanho++; return 0; } int ins_depois (dl_Lista * lista, char *dado, int pos){ int i; dl_elemento *novo_elemento, *em andamento; if ((novo_elemento = aloc (novo_elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); em andamento = lista>início; for (i = 1; i < pos; ++i) em andamento = em andamento>seguinte; novo_elemento>seguinte = em andamento>seguinte; novo_elemento>anterior = em andamento; if(em andamento>seguinte == NULL) lista>fim = novo_elemento; else em andamento >seguinte >anterior = novo_elemento; em andamento >seguinte = novo_elemento; lista>tamanho++; return 0; } int ins_antes (dl_Lista * lista, char *dado, int pos){ int i; dl_elemento *novo_elemento, *em andamento; if ((novo_elemento = aloc (novo elemento)) == NULL) return -1; strcpy (novo_elemento>dado, dado); em andamento = lista>início; for (i = 1; i < pos; ++i) em andamento = em andamento>seguinte; novo_elemento >seguinte = em andamento; novo_elemento > anterior = em andamento>anterior; if(em andamento >anterior == NULL) lista>início = novo_elemento; else em andamento>anterior>seguinte = novo_elemento; em andamento>anterior = novo_elemento; lista>tamanho++; return 0; } int remov(dl_Lista *lista, int pos){ int i; dl_elemento *remov_elemento,*em andamento; if(liste>tamanho == 0) return -1; if(pos == 1){ /* remoção do 1° elemento */ remov_elemento = lista>início; lista > início = lista>início>seguinte; if(lista > início == NULL) lista > fim = NULL; else lista> início > anterior == NULL; } else if(pos == lista>tamanho){ /* remoção do último elemento */ remov_elemento = lista>fim; lista>fim >anterior >seguinte = NULL; lista>fim = lista >fim >anterior; } else { /* remoção em outro lugar */ em andamento = lista >início; for(i=1;i<pos;++i) em andamento = em andamento >seguinte; remov_elemento = em andamento; em andamento >anterior >seguinte = em andamento >seguinte; em andamento >seguinte >anterior = em andamento >anterior; } free(remov_elemento>dado); free(remov_elemento); lista>tamanho--; return 0; } void destruir(dl_Lista *lista){ while(lista>tamanho > 0) remov(lista,1); } dl_elemento *aloc (dl_elemento * novo_elemento){ if ((novo_elemento = (dl_elemento *) malloc (sizeof (dl_elemento))) == NULL) return NULL; if ((novo_elemento >dado = (char *) malloc (50 * sizeof (char))) == NULL) return NULL; return novo_elemento; } int menu (dl_Lista *lista){ int escolha; if (lista>tamanho == 0){ printf ("1. Adição do 1° elemento\n"); printf ("2. Fechar\n"); } else{ printf ("1. Adição no início da lista\n"); printf ("2. Adição no final da lista\n"); printf ("3. Adição antes da posição especificada\n"); printf ("4. Adição depois da posição especificada\n"); printf ("5. Remoção da posição especificada\n"); printf ("6. Destruir a lista\n"); printf ("7. Fechar\n"); } printf ("\n\nFaça sua escolha: "); scanf ("%d", &escolha); getchar(); if(lista >tamanho == 0 && escolha == 2) escolha = 7; return escolha; } int remov(dl_Lista *lista, int pos); void exibe(dl_Lista *lista){ dl_elemento *em andamento; em andamento = lista > início; printf("[ "); while(em andamento!= NULL){ printf("%s ",em anadamento >dado); em andamento = em andamento>seguinte; } printf("]\n"); } void exibe_inv(dl_Lista *lista){ dl_elemento *em andamento; em andamento = lista >fim; while(em andamento != NULL){ printf("%s : ",em andamento >dado); em andamento = em andamento>anterior; } printf("\n"); } /* -------- FIM dlista_function.h --------- */
/* ---------- dlista_function.h ----------- */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "dlista.h" #include "dlista_function.h" int main (void) { int escolha = 0,pos; char *dado; dado = malloc(50); dl_Lista *lista; dl_elemento *pilote = NULL; lista = (dl_Lista *) malloc (sizeof(dl_Lista)); inicialização(lista); while( escolha!= 7){ escolha = menu(lista); switch(escolha){ case 1: printf("Entre um elemento: "); scanf("%s",dado); getchar(); if(lista >tamanho == 0) inserção_em_uma_lista_vazia(lista,dado); else ins_início_lista (lista, dado); printf("%d elementos: início=%s,fim=%s ", lista>tamanho,lista>início>dado,lista>fim>dado); exibe(lista); break; case 2: printf("Entre um elemento: "); scanf("%s",dado); getchar(); ins_fim_lista (lista, dado); printf("%d elementos: início=%s,fim=%s ", lista >tamanho,lista >início>dado,lista>fim>dado); exibe(lista); break; case 3: if(lista >tamanho == 1){ printf("Utilizar a inserção no início ou no fim (Entrar Menu: 1 ou 2)\n"); break; } printf("Entre um elemento:"); scanf("%s",dado); getchar(); do{ printf("Entre a posição:"); scanf("%d",&pos); } while (pos < 1 || pos > lista >tamanho); getchar(); ins_antes(lista,dado,pos); printf("%d elementos: início=%s fim=%s ", lista >tamanho,lista >início>dado,lista>fim>dado); exibe(lista); break; case 4: if(lista >tamanho == 1){ printf("Utilizar a inserção no início ou no fim (Entrar Menu: 1 ou 2)\n"); break; } printf("Entre um elemento : "); scanf("%s",dado); getchar(); do{ printf("Entre a posição: "); scanf("%d",&pos); } while (pos < 1 || pos > lista>tamanho); getchar(); ins_depois(lista,dado,pos); printf("%d elementos: início=%s,fim=%s ", lista >tamanho,lista >início >dado,lista >fim >dado); exibe(lista); break; case 5: do{ printf("Entre a posição: "); scanf("%d",&pos); } while (pos < 1 || pos > posição>tamanho); getchar(); remov(posição,pos); if(posição >tamanho != 0) printf("%d elementos: início=%s,fim=%s ", posição>tamanho,posição >início >dado,posição>fim>dado); else printf("posição vazia: %d elementos",posição >tamanho); exibe(posição); break; case 6: destruir(posição); printf("a posição foi destruída: %d elementos\n",posição >tamanho); break; } } return 0; } /* -------- FIM dlista.c --------- */