l->ptr = l

Anuncio
ESTRUCTURAS DE DATOS
Listas ligadas
Listas ligadas
2



Es una colección lineal de elementos homogéneos.
Lineal significa que cada elemento de la colección
tiene un predecesor (excepto el primero) y un único
sucesor (excepto el último).
El acceso a la lista se efectúa de manera secuencial
Listas ligadas
Listas ligadas (cont.)
3


La lista tiene dos componentes especiales llamados
head y tail. Estos apuntan al primer y último
elemento de la lista
Las listas ligadas son una representación simple de
conjuntos dinámicos y que realiza todas
operaciones : [ Search(), Insert(), Delete(),
Minimum(), Maximum(), Sucessor() y Predecessor()]
Listas ligadas
4
Representación de un elemento para la
lista
typedef struct nodo
{
int valor; //valor o información que guarda el nodo
struct nodo* sig;//referencia al siguiente nodo de
//la lista
} nodo_t;

El nodo consiste de un dato y un apuntador.
valor
sig
Representación de head y tail
5

El apuntador head se declara como un apuntador a
tipo nodo_t y con la ayuda de un auxiliar ptr que
irá desplazándose a través de los elementos de la
lista.
nodo_t *head;
nodo_t *ptr;

Mientras que el tail está implícito en el último
elemento de la lista. El apuntador de ese elemento
apunta a NULL.
Listas ligadas
En resumen
6



La estructura lista tiene dos apuntadores al primero
y al último nodo.
Cada nodo tiene un apuntador al siguiente nodo
El último nodo es un apuntador a NULL
Listas ligadas
Operaciones de la lista
7





Estas operaciones nos facilitarán la
manipulación de los elementos de las listas.
create(): Generar una nueva lista
isEmpty() : Indica si la lista esta vacía.
insert(nodo_t n): Inserta un nuevo
elemento n a la lista (puede tener variantes)
print() : Muestra el contenido de la lista
Listas ligadas
Operaciones de la lista (cont.)
8





freeList(): Vacía la lista.
search(v): Busca un elemento n en la
primera ocurrencia de la lista y devuelve su
posición.
delete(v) : Borra un elemento de la lista
deleteAll(v): Elimina todas la ocurrencias
de v en la lista. (Implementaremos para LLD)
reset() : Lleva el apuntador que recorre la
lista al inicio.
Listas ligadas
create()
9

Se generan la variables apuntador head y ptr y
ambas tendrán la dirección nula (NULL), lo que
indica que tenemos una lista vacía
head
ptr

x
x
El método isEmpty() devolvería 1 a esta nueva
lista.
Listas ligadas
insert()
10

Insertar al inicio de una lista
 Si
es una lista vacía, luego de crear el nuevo nodo
hacemos que head y ptr apunten al nuevo nodo
ptr
head
ptr
x
x
head
 Si
9
contiene al menos un elemento, head apunta a la
dirección de memoria del nuevo nodo, el apuntador sig
va a ser el mismo que ptr
Listas ligadas
insert() al inicio
11
ptr
ptr
head

9
head
3
9
El desarrollo de la función insert() que recibe
por referencia el puntero al primer nodo de lista
más un valor de tipo int para agregarlo como el
primer elemento.
Listas ligadas
insert() al final
12
Al final de la lista
 La
idea es recorrer la lista avanzando sobre cada uno
de sus nodos hasta llegar al último.
 El último se identifica por el valor NULL en su
referencia al siguiente nodo.
3
ptr
5
7
1
6
head
 Para
ello utilizaremos el apuntador ptr y le
asignamos la dirección contenida por head.
Listas ligadas
insert() al final
13

Para saber si ptr apunta al último nodo preguntamos si
ptr->sig es NULL. Observando la figura, ptr->sig
(el siguiente de ptr) no es NULL ya que tiene la dirección
del nodo con valor 7
ptr
head

5
7
1
6
El próximo paso será hacer que ptr apunte al siguiente
nodo: ptr = ptr->sig
ptr
head
5
7
1
6
insert() al final
14

En realidad, este proceso lo haremos dentro de un
ciclo de repeticiones que itere mientras que “el
siguiente de ptr” sea distinto de NULL
//:
l->ptr = l->head;
while((l->ptr)->sig != NULL)
{
l->ptr = l->ptr->sig;
}//:

ptr comienza apuntando al primer nodo de la lista
y en cada iteración apuntará al siguiente. La
condición del while se dejará de cumplir cuando
ptr apunte a un nodo sin elemento.
insert() al final
15
ptr
3
head
5
7
1
6
El último paso será crear un nuevo nodo y enlazarlo al final. Esto es
convertirlo en el siguiente del último nodo de la lista.
ptr
head
5
7
1
6
3
//creamos un nuevo nodo
nodo_t* nuevo = (nodo_t*)malloc(sizeof(nodo_t));
//asignamos su valor y NULL en su siguiente
nuevo->valor=3;
nuevo->sig = NULL;
//lo enlazamos como siguiente de ptr
l->ptr->sig = nuevo;
void insertAtTail(list_t *l, int v)
{
node_t *nuevo = (node_t *)malloc(sizeof(node_t));
nuevo->valor = v;
nuevo->sig
= NULL;
if(l->head == NULL )
{
//primer elemento de l
l->head = nuevo;
}
else
{
l->ptr = l->head;
while((l->ptr)->sig != NULL)
{
l->ptr = l->ptr->sig;
}
l->ptr->sig = nuevo;
}
}
// Enlazar el nuevo nodo
print()
18

Hacemos un recorrido a la lista no vacía e
imprimimos el valor almacenado. Nos posicionamos
en el primer nodo, mostramos su valor y avanzamos
al siguiente.
ptr
x
5
7
1
head
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
print()
19

ptr
Efectuando debugging
5
7
1
head
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
ptr
5
7
1
head
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
Listas ligadas
print()
20

Entrando al while
ptr
5
7
ptr
1
head
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
5
7
1
head
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
Listas ligadas
print()
21

Continuamos con el debugging ilustrado
ptr
head
5
7
ptr
1
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
head
5
7
1
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
Listas ligadas
print()
22
ptr
head
5
7
1
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
ptr
head
5
7
1
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
Listas ligadas
print() final debugging
23
ptr
head
5
7
NULL
1
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
ptr
head
5
7
NULL
1
l->ptr = l->head;
while(l->ptr != NULL)
{
printf(" %d ",l->ptr->valor);
l->ptr = l->ptr->sig;
}
Listas ligadas
print() diagrama de flujo
24

En la función, simplemente recibimos el puntero al
primer nodo de la lista (del tipo nodo_t*), Esto es
porque no vamos a modificarlo.
print
list_t *l
l->ptr ← l->head
l->ptr != NULL
l->ptr->valor
l->ptr ← ptr->sig
freeList()
25

La memoria gestionada por malloc es persistente y
permanece asignada durante la ejecución del
programa. Por ello es nuestra responsabilidad liberar la
memoria cuando ya no la necesitemos
freeList
list_t* l
l->head !=NULL
prox ← (l->head)->sig
free(l->head)
l->head ← prox
freeList()
26




La función libera la memoria que utiliza la lista
ligada direccionada por head. Al finalizar, debe
asignar el valor NULL a head.
La lista quedará vacía. Por ello hay que recibir a
head por referencia (list_t *l)
La función finaliza cuando head tenga el valor
NULL.
Como l->head es la dirección del primer nodo
entonces (l->head)->sig es la dirección del
segundo
Listas ligadas
freeList()
27



La asignación prox=(l->head)->sig asigna a
la variable prox la dirección del segundo elemento
de la lista.
Los paréntesis se colocan ya que el operador ->
tiene precedencia sobre el operador * y si no lo
utilizamos estaríamos hablando del “siguiente de
head” y el apuntador no tiene ese campo.
Después de la asignación
prox=(l->head)->sig podemos liberar la
memoria asignada por l->head
Listas ligadas
freeList()
28
prox
head


5
7
1
Ahora debemos hacer que el primer nodo de la
lista sea el que está siendo apuntado por prox.
La asignación l->head=prox descarta,
definitivamente, el primer nodo (ya liberado
mediante la función free) y hace que la lista
comience desde el segundo elemento.
Listas ligadas
freeList()
29
prox
5
7
1
head


El hecho de liberar con free no implica que la
información se haya borrado.
El espacio de memoria ya no pertenece más a
nuestro programa y, no tenemos formar de
accederlo porque no lo tenemos apuntado con
ningún puntero. Quedó des referenciado.
Listas ligadas
void freeList(list_t *l){
while (l->head != NULL){
nodo_t* prox = (l->head)->sig;
free(l->head);
l->head = prox;
}
}

30
Cuando prox apunte al último nodo de la lista la
asignación
l->head = prox estaremos
asignando NULL a head con lo que, finalmente, la
lista quedará vacía y la memoria que ocupaba
estará liberada.
Listas ligadas
search(v)
31

Vamos a determinar si la lista contiene un valor
determinado v
 Búsqueda
secuencial en los valores de los nodos
 Si existe el valor en la lista se entrega la dirección en
memoria del nodo
 Puede darse el caso que no exista el elemento en la
lista, por lo que la función podría arrojarnos NULL
como resultado.
Listas ligadas
search(v)
32

Para la lista ligada de
la figura, la llamada a
search(l,7)
regresa un puntero al
segundo elemento, y la
llamada a
search(l,9)
retorna NULL
ptr
5
7
1
head
search
list_t *l, int v
l->ptr ← l->head
l->ptr!=NULL && l->ptr->val!= v
l->ptr ← ptr->sig
return ptr
Listas ligadas
delete(v)
33



La función delete(v) desreferencia, un elemento
que contiene el valor v, de la lista ligada.
Con la ayuda de un apuntador aux, ptr recorre la
lista hasta encontrar el elemento con el valor
indicado, aux recorre la lista pero quedando
referenciado a un elemento atrás del que apunta
ptr.
O podría resolverse más sencillo si los elementos de
la lista apunta tanto al predecesor como al
siguiente.
Listas ligadas
delete(v)
34
Se utilizan dos apuntadores: uno
al nodo que se va a elimiar(aux)
y otro que apunte al nodo
anterior(ant).
Listas doblemente ligadas
35



Similar a la estructura de una lista ligada simple.
Tienen dos apuntadores, al nodo anterior y al nodo
siguiente.
Mejora los algoritmos de los métodos definidos
para un lista ligada simple.
Nodo
sig
valor
prev
Listas doblemente ligadas
El apuntador al anterior del primer nodo y el
apuntador al siguiente del último son NULL.
La lista puede recorrerse en ambas direcciones.
Las operaciones insertar y eliminar utilizan menos
instrucciones.
También se manejan dos apuntadores p y q que
apuntan al primer y al último nodo.
Listas doblemente ligadas
Para diseñar un algoritmo que defina el
comportamiento de una Listas Doblemente Ligadas
se deben considerar 2 casos para cada operación
(buscar, insertar y eliminar):


Estructura vacía (caso extremo).
Estructura con elemento(s) (caso base).
Lista doblemente ligada circular


Una lista doblemente ligada circular (o lista doble circular) es una
lista doblemente ligada modificada, donde la referencia siguiente
(NEXT) del elemento que se encuentra al final de la lista (tail) en
lugar de apuntar a nulo, apunta al primer elemento de la lista
(head).
Es posible recorrer la lisa a través de la referencia al predecesor
(PREV) de cada nodo, hay que tener en cuenta el número de
elementos de la lista, ya que el primer elemento apunta al final de
la estructura y, por tanto, se puede recorrer de manera infinita.
head
tail
PREV
NEXT
Descargar