
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 --------- */