You are on page 1of 41

FACULTAD DE INGENIERÍA

Escuela Profesional de Ingeniería de Sistemas y


Computación

Asignatura: Algoritmos y Estructuras de Datos

Mg. Jaime Ortíz Fernández


e-mail: d.jortiz@upla.edu.pe

HUANCAYO - 2022
UNIDAD II COLAS Y RECURSIVIDAD

TEMA: Listas Simplemente Enlazadas


(LSE)

Diapositivas: Mg. Jaime Ortíz Fernández


LISTAS: DEFINICIÓN
Una lista enlazada es un tipo de dato autorreferenciado porque contiene
un puntero o enlace (en inglés link, del mismo significado) a otro dato del
mismo tipo. Las listas enlazadas permiten inserciones y eliminación de
elementos o nodos en cualquier punto de la lista en tiempo constante
(suponiendo que dicho punto está previamente identificado o localizado),
pero no permiten un acceso aleatorio.
Existen diferentes tipos de listas enlazadas: listas enlazadas simples,
listas doblemente enlazadas, listas enlazadas circulares y listas enlazadas
doblemente circulares.
LISTAS: DEFINICIÓN
Una lista es
• Una colección de 0 o más elementos
• Si la lista no tiene elementos, se dice que esta vacía
• En una lista, todos los elementos son de un mismo tipo

Son estructuras lineales, es decir


• Sus elementos están colocados uno detrás de otro
• Cada elemento de una lista se conoce con el nombre de NODO

Las listas
• Son mucho más flexibles que los arreglos
• Permiten trabajo “dinámico” con un grupo de elementos
Cuando se construye y se utiliza una lista enlazada en una
aplicación, el acceso a la lista se hace mediante una o más
referencias a los nodos (punteros o apuntadores).
Normalmente, se accede a partir del primer nodo de la lista,
llamado cabeza (head) o cabecera de la lista.
Una referencia al primer nodo se llama referencia cabeza.
En ocasiones, se mantiene también una referencia al último nodo de la lista
enlazada. El último nodo es la cola (last) de la lista, y una referencia al último
nodo es la referencia cola.

head last
TIPOS
De acuerdo a su comportamiento, los conjuntos lineales se clasifican en
• Listas, Pilas y Colas

De acuerdo a su implementación, las listas se clasifican en


• Simples o simplemente enlazadas
• Doblemente Enlazadas
• Circulares simplemente enlazadas
• Circulares doblemente enlazadas
LISTAS SIMPLES
Se define como un conjunto de nodos
• Uno detrás de otro
• Del cual siempre se puede conocer al nodo inicial y al nodo final

De cada nodo de la lista, se conoce


• Un contenido, que es la información que almacena dentro
 Puede ser de cualquier tipo de dato (un datos simple, varios datos
simples, un objeto, varios objetos, combinación de objetos y datos
simples)
• Un sucesor único
 Excepto el ultimo nodo de la lista
LISTA SIMPLE: NIVEL LÓGICO
Operaciones o Comportamiento (con una lista se puede) del TDA Lista
• Crear y Eliminar la lista
• Conocer si la lista está vacía
• Añadir elementos y removerlos
• Consultar el primer y al ultimo elemento
• Imprimir sus elementos en pantalla
• Buscar un elemento con cierta información en la lista
Definición gramatical
<listaSimple> ::= <comienzo> + {<ref_nodo>} + <final>
<comienzo> ::= <enlace>
<final> ::= <enlace>
TDA NODO: NIVEL LÓGICO
Una lista esta compuesta de nodos, y por eso es importante definir el TDA Nodo
Un nodo de una lista
• Almacena información de cualquier tipo y
• Es capaz de “viajar” o conocer otro nodo(el nodo siguiente), mediante un puntero
Operaciones o Comportamiento
• Crear y Eliminar
• Consultar y modificar la información que almacena
• Consultar y modificar el enlace que mantiene con otro nodo

<nodo> ::= <contenido> + <enlace>


<contenido> ::= <dato>{<dato>}
<enlace> ::= <ref_nodo> | <ref_invalida>
LISTAS SIMPLES: NIVEL DE
IMPLEMENTACION
Tenemos el concepto claro de lo que debe ser una lista

Ahora debemos ir al detalle: como darle vida a una lista

Hay varias posibilidades de implementación


• Estática o Contigua, usando arreglos de longitud “variable”
• Dinámica, utilizando punteros
IMPLEMENTACION CONTIGUA
Se utilizan arreglos, por lo tanto
• Tiene límites, que no pueden ser rebasados al añadir nuevo elementos
• Los “nodos” son adyacentes en memoria
• Cada nodo es realmente un elemento del arreglo
• Entonces, el enlace con el siguiente nodo seria simplemente el indice del siguiente elemento
dentro del arreglo
• NOTA: En este tipo de implementación no es necesario crear el TDA Nodo
Al crearla se debe indicar el tamaño máximo del arreglo
Al insertar o remover un elemento,
• Todos los elementos restantes avanzarán o retrocederán
No es la implementación ideal para las listas simples

Al insertarse un
nuevo elemento,
25
en una cierta
posición, todos los 10 5 8 25
2 2
31 31
elementos
restantes ruedan
0 1 2 3 4 5 6 7 8
En uso Desperdicio
CONTIGUAS: DECLARACION
La lista contigua
• Es un arreglo de elementos de cualquier tipo  realmente es un ARRAY
• No olvidemos que hay que predefinir el máximo de nodos de la lista
Para lograr mantener control sobre la cantidad de elementos en la lista
• Debe existir una referencia que controle el ultimo elemento
• La referencia al ultimo se moverá de acuerdo a las inserciones y
eliminaciones
• La primera referencia nunca se mueve, siempre será 0

header=0 last MAX


class LSCont{
int ultimo; 10 5 8 25 2 31
ArrayU Elementos;
} 0 1 2 3 4 5 6 7 8

Elementos
CONTIGUA: BASICAS
last last
El índice del último elemento
debe ser un índice inválido,
un valor negativo. No
importará que el arreglo
tenga elementos pues no -1 0 1 2 3 4 5 6 7 8 9
serán tomados en cuenta.
MAX-1
void LSCont_VaciarLista(LSCont L){
L->ultimo = -1;
}
bool LSCont_EstaVacia(LSCont L){
LSCont LSCont_CrearLista(int max){
return(L.ultimo<0);
LSCont nuevalista;
}
nuevalista->Elementos =
ArrayU_Crear(max, 0); bool LSCont_EstaLlena(LSCont L){
nuevalista->ultimo = -1;
return (L.ultimo == ArrayU_Tamanio(L.Elementol)-1);
return nuevalista;
} }
void LSCont_EliminarLista(LSCont L){
ArrayU_Eliminar(&(L->Elementos)); Si la lista está llena es
porque el índice del último ha
LSCont_VaciarLista(L); llegado al verdadero último
} índice posible en el arreglo:
MAX -1
CONTIGUA: BÚSQUEDA

int LSCont_BuscarNodo (LSCont L, Generico G, Generico_Comparacion fn){


int i;
Generico elemento;
for(i = 0; i <=L.ultimo;i++){
elemento = ArrayU_ElemC(L.Elementos, i);
if(f(elemento, G) == 0) return (i)
}
return(-1);
}
CONTIGUA: INSERTAR
9 last last boolean LSCont_Insertar (LSCont L, int P, Generico G){
int i,
10
9 1
10 5
1 8
5 8 Generico ele1;
if(LSCont_EstaLlena(L)) return FALSE;
0 1 2 3 4 5 6 7 8 9 if(P<=-1) return FALSE;
boolean LSCont_InsertarInicio (LSCont L, Generico G){ //MOVER TODOS
for(i = L->ultimo; i >=P ;i--){
int i;
ele1 = ArrayU_ElemC(L->Elementos,i);
Generico ele1, ele2; ArrayU_ElemM(L->Elementos,i+1, ele1);
//No insertar si ya está llena }
if(LSCont_EstaLlena(L)) return FALSE; ArrayU_ElemM(L->Elementos, P, G);
//Mover todo hacia delante L->utlimo ++;
for(i = L->ultimo; i >=0 ;i--){ return TRUE;
}
ele1 = ArrayU_ElemC(L->Elementos,i);
ArrayU_ElemM(L->Elementos,i+1, ele1);
}
ArrayU_ElemM(L->Elementos, 0, G); 9 last last
L->ultimo++;
return(TRUE); 10 11
9 5
1 8
5 8
}
0 1 2 3 4 5 6 7 8 9
CONTIGUA: Eliminar
boolean LSCont_EliminarNodo(LSCont L, int P){
int i;
Generico ele1;
if(LSCont_EstaVacia(L)) return FALSE;
if(P<=-1) return FALSE Eliminar por Info, dada
una cierta información,
//RETROCEDER TODOS buscar el elemento que
for(i = P; i < L->ultimo;i++){ la tenga y eliminarlo
ele1 = ArrayU_ElemC(L->Elementos, i+1);
ArrayU_ElemM(L->Elementos, i, ele1);
}
L->last --;
return TRUE; boolean LSCont_EliminarxInfo(LSCont L, Generico G){
} int pos;
if(LSCont_EstaVacia(L)) return FALSE;
pos = LSCont_Localizar(L, G);
last last if(pos >= 0)
return(LSCont_EliminaNodo(L, pos););
10 1
5 5
8 8 }

0 1 2 3 4 5 6 7 8 9
LSE: LISTAS SIMPLES ENLAZADAS
Es una implementación flexible y potente
C S
Contenido Enlace
Los nodos ya no son adyacentes en memoria
NODO B
• Un nodo A logra un enlace con otro nodo B,
C S
• Almacenando dentro la dirección de memoria de B
NODO A
Al insertar o eliminar un nodo ya no hay que “mover” al resto de
elementos, solo hay que enlazarlo con la lista

25

25

10 5 8 25
2 2
31 31
TDA LSE_NODO: NIVEL DE
IMPLEMENTACION
Contenido: Datos enteros, reales, o incluso, de Estructuras o clases:
Estudiante, Auto, etc....
Y además, el nodo también contiene un enlace con su nodo siguiente
• Este enlace, puede no enlazar el nodo con nadie, el nodo esta solito,
no forma parte de ninguna lista
• O puede “apuntar” a otro nodo, indicando que ese es su siguiente nodo,
formando una Lista
Modelo de una Lista Simplemente enlazada que almacena números reales. Se
menciona la cabeza (head) de la lista que es la referencia del primer nodo, y la cola
(last) de la lista, que es una referencia al último nodo es la referencia cola.

head last
Class Automovil{
class Nodo{ Nodo cabeza,
String marca;
Automovil a; // dato del elemento
String modelo;
Nodo enlace; Nodo cola;
double costo;
}
String placa;
}

Una cola con varios datos, solo cambia el Nodo


class Nodo{
String nombre;
int edad;
String sexo;
Nodo enlace
}
Cada referencia a un nodo debe ser declarada como una variable
referencia. Por ejemplo, si se mantiene una lista enlazada con
una referencia de cabecera y otra de cola, se deben declarar dos
variables referencia:
Nodo cabeza;
Nodo cola;
Para tener en cuenta:
La construcción y manipulación de una lista enlazada
requiere el acceso a los nodos de la lista a través de
una o más referencias a nodos. Normalmente, se
incluye una referencia al primer nodo (cabeza) y
además, en algunas aplicaciones, una referencia al
último nodo (cola).
La diapositiva siguiente muestra una lista a la que se accede con la referencia cabeza;
cada nodo está enlazado con el siguiente nodo.
El último nodo, cola o final de la lista, no se enlaza con otro nodo, por lo que su campo
de enlace contiene la referencia nulo, null.
La palabra null representa la referencia nulo, que es una constante especial de Java.
Se puede utilizar null para cualquier valor de referencia que no apunte a objeto alguno.
La referencia null se utiliza, normalmente, en dos situaciones:
• En el campo enlace del último nodo (final o cola) de una lista enlazada.
• Como valor de la referencia cabeza para una lista enlazada que no tiene nodos. Tal lista se
denomina lista vacía (cabeza = null).
La referencia null se puede asignar a una variable referencia con
una sentencia de asignación ordinaria. Por ejemplo:
Nodo cabeza;
cabeza = null;

Nota de programación
La referencia cabeza (y cola) de una lista enlazada, normalmente, se inicializa a
null, indica lista vacía (no tiene nodos), cuando se inicia la construcción de una
lista.
Cualquier método que se escriba para implementar listas enlazadas debe poder
manejar un referencia de cabeza (y de cola) null.
TDA LSE_NODO: OPERACIONES

Crear y Eliminar
• LSE_nodo LSE_Nodo_Crear(Generico G);
• void LSE_nodo_Eliminar (LSE_nodo p);

Consultar y Modificar Contenido


• Generico LSE_nodo_GetContenido(LSE_nodo p);
• void LSE_nodo_SetContenido(LSE_nodo p, Generico G);

Consultar y Modificar Enlaces


• LSE_nodo LSE_nodo_Siguiente(LSE_nodo p);
• void LSE_nodo_SetSiguiente(LSE_nodo p, LSE_nodo Enlace);
TDA LSE_NODO: DECLARACION
Un nodo dinámico almacena dentro
• Un contenido de cualquier tipo de dato, entero, real, estructuras,clases,
etc......
• Un enlace, que es la referencia al nodo siguiente, la dirección del nodo
siguiente

class TLSE_Nodo LS_Nodo{


Generico G;
sNodo sig;
}
LSE_Nodo LSE_NodoPtr;
LSE_NODO: CREAR Y DESTRUIR
Al crear un nodo se le Al eliminar un nodo se liberará
asignará un valor inicial la memoria que este ocupa

LSE_nodo LSE_CrearNodo(Generico G) void LSE_EliminarNodo(LSE_nodo p)


{ {
LSE_nodo p; if(p)
p = (LSE_nodo )malloc(sizeof(LSE_nodo)); {
if(p) free(p);
{ p = NULL;
p->G = G; }
p->sig = NULL; }
}
return p;
}
LSE: NIVEL DE IMPLEMENTACION
En una lista hay que llevar control de las posiciones al primer y el último
elemento
En la lista, las posiciones son direcciones de memoria: punteros
• Header y Last son punteros a Nodo en una lista enlazada

La posición de un nodo estará dada por un puntero a dicho nodo


Una lista enlazada no tiene datos predefinidos
• Los elementos o Nodos van siendo creados y eliminados a medida que se va
necesitando
LSE: OPERACIONES
Crear y Eliminar
• LSE LSE_CrearLista();
• void LSE_Eliminar(LSE L);
Consultar Primer y Ultimo
• LSE_nodo LSE_NodoInicio(LSE L);
• LSE_nodo LSE_NodoFin(LSE L);
Conocer Estado
• boolean LSE_EstaVacia(LSE L);
Añadir y Remover
• boolean LSE_InsertarNodoInicio(LSE L, LSE_nodo pNodo);
• boolean LSE_InsertarNodoFin(LSE L, LSE_nodo pNodo);
• boolean LSE_Insertar(LSE L, LSE_nodo p, LSE_nodo nuevo);
• LSE_nodo LSE_SacarNodoInicio(LSE L);
• LSE_nodo LSE_SacarNodoFin(LSE L);
• bool LSE_EliminarxPos(LSE L, LSE_nodo p);
Busqueda y Recorrido
• LSE_nodo LSE_BuscarNodo(LSE L, Generico G, Generico_fnComparar f);
• boolean LSE_ExisteNodo(LSE L, LSE_nodo p);
• void LSE_Recorrer(LSE L, Generico_fnImprimir f);
LSE: DECLARACIÓN
Hay varias formas de definir una lista
• Solo a través del primer elemento a la misma

LSE_Nodo LSE;

O llevando control del primero y el último elemento


public class LSE {
LSE_Nodo header;
LSE_Nodo last;
}
LSE: CREAR y DESTRUIR
Al crear una lista, ésta Al Eliminar una lista, cada uno
estará vacía, su primero y de los nodos debe ser liberado
ultimo no apuntaran a nadie

LSE LSE_CrearLista(){ void LSE_Vaciar(LSE L){


LSE lnueva; LSE_nodo p;
lnueva = malloc(sizeof(LSE)); while(!LSE_EstaVacia(L)){
lnueva->header = lnueva->last = NULL; p = LSE_SacarNodoFin(L);
return lnueva; LSE_Nodo_Eliminar(p);
} }
}

header
last
LSE: BUSQUEDA LSE_nodo LSE_BuscarNodo(LSE L,
Generico G, Generico_fnComparar f){
Hay que ubicarse en el inicio: header LSE_nodo p;
for(p = L.header; p!= NULL; p = LSE_Nodo_Siguiente(p)){
E ir avanzando hasta if(f(LSE_Nodo_Contenido(p),G) ==0) return(p);
• Encontrar el nodo con la información }
buscada o return(NULL);
}
• Que ya no haya más nodos

Como no se usan índices, se usan punteros:


Recomendación:
Usemos el
• Un puntero se ubicará en el header
comportamiento de
• Y luego irá avanzando al siguiente, y al
LSE_Nodo, no el
siguiente y al siguiente
estado
Al encontrar al nodo buscado, no se retorna su
posición como índice, esta no importa
Busco 30
25
Se retorna la dirección de este nodo(puntero)

header last

10 5 8 25 2 31

pp pp pp pp p p
p
LSE: ANTERIOR
Dada la dirección de un nodo(pos), esta función retorna la dirección del nodo La
anterior posición
p es la
Hay que “buscar” desde el header de un
nodo
El nodo buscado es aquel cuyo siguiente es igual a pos fuera de
la lista
Si el elemento buscado es el primero en la lista, no hay anterior
p
Ejemplo al
7
LSE_nodo LSE_Anterior(LSE L, LSE_nodo p){ usar el
LSE_nodo q; estado de
if(LSE_EstaVacia(L)) return NULL; LSE_Nodo
if(L.header == p) return NULL;
header last
for(q=L.header; q!=NULL;q=q->sig){
if(q->sig ==p) return(q);
10 5 8 25 2 31
}
return(NULL);
qq q qq qp q q
q q
}
LSE: PRIMERO Y ULTIMO
Se pueden obtener siempre y cuando la lista no este vacía
Retornarán la información del elemento apuntado por header y
last respectivamente.

LSE_nodo LSE_NodoInicio(LSE L){ LSE_nodo LSE_NodoFin(LSE L){


return (L.header); return (L.last);
} }
LSE: INSERTAR
La operación de insertar recibirá
• El NODO en si que se va a insertar
• Este nodo ya debió haber sido creado antes de
insertarlo

Hay varios tipos de inserción


• Insertar al inicio o al final
• Insertar en medio de la lista (en cualquier posición)
INSERCION AL INICIO/FINAL last->sig = nuevo;

Si la lista está vacía, tanto header como last apuntan al last = nuevo;
nuevo nodo
Si no, si es al inicio el nuevo header, es el nuevo nodo last
Si no, si es al final, el nuevo last es el nuevo nodo
nuevo

header 18
header last
nuevo
10 5 8 25 2 31
9

boolean LSE_InsertarNodoInicio(LSE L, boolean LSE_InsertarNodoFin(LSE L, LSE_nodo


nuevo->sig = header; nuevo){
LSE_nodo nuevo){
if (!nuevo) return FALSE; if(!nuevo) return FALSE;
if(LSE_EstaVacia(L)){ if(LSE_EstaVacia L)){
header = nuevo; L->header = L->last = nuevo;
L->header = L->last = nuevo;
} else{ } else{
LSE_Nodo_ModificarEnlace( LSE_Nodo_ModificarEnlace(L->last,, nuevo);
nuevo, L->header); L->last = nuevo;
L->header = nuevo; }
} return(TRUE);
return(TRUE);
} }
boolean LSE_Insertar(LSE L,
INSERTAR EN MEDIO LSE_nodo p,
LSE_nodo nuevo){
if(L->last==p){//Insertar al final
return(LSE_InsertarNodoFin(L, nuevo));
Debe recibir la posición donde se va a insertar }else{
if(!p || ! LSE_ExisteNodo(L,p))
Insertemos después return FALSE;
if(LSE_EstaVacia(L))
• Si Lista Vacía, el nuevo header y last, es el L->header = L->last = nuevo;
nuevo nodo else{
LSE_Nodo_ModificarEnlace(
• Si la posición no existe no efectuar la
nuevo, LSE_Nodo_Siguiente(p));
inserción LSE_Nodo_ModificarEnlace(p, nuevo);
• Si la lista solo tiene un elemento, el nuevo }
}
last es el nuevo nodo return(TRUE);

nuevo
header = last = nuevo:
last p->sig = nuevo; 18 Nuevo->sig = p->sig;
header
last header
header p last
nuevo
10 5 8 25 2 31
18
LSE: ELIMINAR DE LA LISTA
La lista no debe estar vacía!!, si tiene un solo elemento, dejarla vacía

Header = header->sig; tmp = last;


header header last last
Last = Anterior(Last);
Last->sig = NULL;
Tmp = header; 10 5 8 25 2 31
free(tmp);
free(tmp); tmp
tmp
LSE_nodo LSE_SacarNodoFin(LSE L){
LSE_nodo LSE_SacarNodoInicio(LSE L){ LSE_nodo tmp=L->header;
LSE_nodo tmp = L->header; if(LSE_EstaVacia(L)) return NULL;
if(LSE_EstaVacia(L)) return NULL; if(L->header == L->last)
if(L->header == L->last) LSE_InicializarLista(L);
LSE_InicializarLista(L); else{
else { tmp = L->last;
L->header = L->header->sig; L->last = LSE_Anterior(L, L->last);
} L->last->sig = NULL;
return(tmp); }
return(tmp);
}
}
LSE: ELIMINAR JUSTO UN NODO
Lista no debe estar vacía boolean LSE_EliminarxPos(LSE L, LSE_nodo p){
LSE_nodo p_ant;
if(LSE_EstaVacia(L)) return 0;
La posición enviada debe existir en la lista if(!p || !LSE_ExisteNodo(L, p))
return FALSE;
Revisar si se desea eliminar el header o el if(p==L->header)
last LSE_SacarNodoInicio(L);
else if(p == L->last)
LSE_SacarNodoFin(L);
else{
p_ant = LSE_Anterior(L,p);
p_ant->sig = p->sig; p_ant->sig = p->sig;
p->sig = NULL;
}
return(TRUE);
} last
header p

10 5 8 25 2 31

p_ant = Anterior(p); pant free(p);


VISUALIZAR
Imprimir todos los nodos de la lista

void LSE_Recorrer(LSE L, Generico_fnImprimir fn){


LSE_nodo p;
for( p = L.header; p!=NULL; p = p->sig)
fn(p->G);

}
¿Preguntas?

You might also like