2. OBJETIVOS
Aprovechar la abstracción para definir
comportamiento y luego operaciones en nivel de
implementación
Visualizar las posibles implementaciones de un
TDA ya definido
Utilizar con seguridad el concepto de puntero en
implementaciones dinámicas
Reconocer la importancia de usar solo el
comportamiento de un TDA, y no su estado
3. LISTAS: DEFINICION
Una lista es
Una colección de 0 o mas 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
4. 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
Doblemente Enlazadas
Circulares
5. 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 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 sucesor único
Excepto el ultimo nodo de la lista
6. LISTA SIMPLE: NIVEL LOGICO
Comportamiento (a/con una lista se puede)
Crear y Eliminar
Conocer si esta 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
Estado:
<listaSimple> ::= <comienzo> + {<ref_nodo>} + <final>
<comienzo> ::= <enlace>
<final> ::= <enlace>
7. TDA NODO: NIVEL LOGICO
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 dentro y
Es capaz de “viajar” o conocer otro nodo(el nodo siguiente)
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>
8. 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
9. IMPLEMENTACION CONTIGUA
Se utilizan arreglos, por lo tanto
Tiene limites, 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
OJO: 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 implementacion ideal para las listas simples
Al insertarse un
nuevo elemento,
25
en una cierta
posición, todos los 10 5 8 2
25 31
2 31
elementos
restantes ruedan
0 1 2 3 4 5 6 7 8
En uso Desperdicio
10. CONTIGUAS: OPERACIONES
Creación y Eliminación Busqueda y Recorrido
LSCont *LSCont_Crear(int n); int LSCont_BuscarNodo(LSCont L, Generico
void LSCont_Vaciar(LSCont *L); G, Generico_ComoComparar fn);
Añadir y Remover elementos
bool LSCont_EsNodoDeLista(LSCont L, int i);
void LSCont_InsertarNodoInicio(LSCont *L,
void LSCont_Recorrer(LSCont L,
Generico G); Generico_ComoImprimir fn);
void LSCont_InsertarNodoFin(LSCont *L,
Generico G);
Generico LSCont_SacarNodoInicio(LSCont *L);
Generico LSCont_SacarNodoFin(LSCont *L);
Consultar indice del Ultimo
int LSCont_ConsultarUltimo(LSCont L);
Consultar estado de la lista, puede llenarse
bool LSCont_EstaVacia(LSCont L);
bool LSCont_EstaLlena(LSCont L);
11. CONTIGUAS: DECLARACION
La lista contigua
Es un arreglo de elementos de cualquier tipo realmente es un ARRAYU
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 primero nunca se mueve, siempre será 0
header=0 last MAX
typedef struct{
int ultimo; 10 5 8 25 2 31
ArrayU Elementos;
0 1 2 3 4 5 6 7 8
}LSCont;
Elementos
12. CONTIGUA: BASICAS last last
El índice del último
elemento debe ser un índice
inválido, un valor negativo.
No importara 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;
}
LSCont *LSCont_CrearLista(int max){ bool LSCont_EstaVacia(LSCont L){
LSCont *nuevalista; return(L.ultimo<0);
nuevalista->Elementos = }
ArrayU_Crear(max, 0); bool LSCont_EstaLlena(LSCont L){
nuevalista->ultimo = -1;
return (L.ultimo ==
return nuevalista;
ArrayU_Tamanio(L.Elementol)-1);
}
void LSCont_EliminarLista(LSCont *L){ }
ArrayU_Eliminar(&(L->Elementos));Si la lista está llena es
porque el índice del último
LSCont_VaciarLista(L); ha llegado al verdadero
último índice posible en el
} arreglo: MAX -1
14. CONTIGUA: INSERTAR
9 last last bool LSCont_Insertar(LSCont *L, int P, Generico G)
{
int i,
10
9 10
1 1
5 5
8 8
Generico ele1;
0 1 2 3 4 5 6 7 8 9 if(LSCont_EstaLlena(L)) return FALSE;
if(P<=-1) return FALSE;
bool LSCont_InsertarInicio(LSCont *L, Generico G){
//MOVER TODOS
int i;
for(i = L->ultimo; i >=P ;i--){
Generico ele1, ele2; ele1 = ArrayU_ElemC(L->Elementos,i);
//No insertar si ya esta llena ArrayU_ElemM(L->Elementos,i+1, ele1);
if(LSCont_EstaLlena(L)) return FALSE; }
//Mover todo hacia delante ArrayU_ElemM(L->Elementos, P, G);
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 9
1
1 1
5 5
8 8
}
0 1 2 3 4 5 6 7 8 9
15. CONTIGUA: SACAR
bool LSCont_EliminarNodo(LSCont *L, int P){
int i;
Generico ele1;
if(LSCont_EstaVacia(L)) return FALSE;
if(P<=-1) return FALSE
//RETROCEDER TODOS
for(i = P; i < L->ultimo;i++){ Eliminar por Info, dada
una cierta información,
ele1 = ArrayU_ElemC(L->Elementos, i+1); buscar el elemento que
ArrayU_ElemM(L->Elementos, i, ele1); la tenga y eliminarlo
}
L->last --;
return TRUE; bool 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 5
1 8
5 8 }
0 1 2 3 4 5 6 7 8 9
16. 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 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
elemento, solo enlazarlo con la lista
25
25
10 5 8 25
2 2
31 31
17. TDA LSE_NODO: NIVEL DE
IMPLEMENTACION
Contenido: Datos enteros, reales, o incluso, de Estructuras: 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, formando
una Lista
19. TDA LSE_NODO:
DECLARACION
Un nodo dinámico almacena dentro
Un contenido de cualquier tipo de dato, entero, real, estructuras, etc......
Un enlace, que es la referencia al nodo siguiente, la dirección del nodo
siguiente
typedef struct TLSE_Nodo{
Generico G;
struct TLSE_Nodo *sig;
} LSE_Nodo;
typedef LSE_Nodo * LSE_NodoPtr;
20. LSE_NODO: CREAR Y
DESTRUIR
Al crear un nodo se le Al eliminar un nodo se
asignara un valor inicial liberara la memoria que este
ocupa
LSE_nodo *LSE_CrearNodo(Generico G)
{
void LSE_EliminarNodo(LSE_nodo *p)
LSE_nodo *p;
{
p = (LSE_nodo *)malloc(sizeof(LSE_nodo)); if(p)
if(p) {
{ free(p);
p->G = G; p = NULL;
p->sig = NULL; }
} }
return p;
}
21. 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
23. LSE: DECLARACIÓN
Hay varias formas de definir una lista
Solo a través del primer elemento a la misma
typedef LSE_Nodo * LSE;
O llevando control del primero y el último elemento
typedef struct{
LSE_Nodo *header;
LSE_Nodo *last;
}LSE;
24. LSE: CREAR y DESTRUIR
Al crear una lista, esta estará Al Eliminar una lista, cada
vacía, su primero y ultimo no uno de los nodos debe ser
apuntaran a nadie liberado
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 = p = LSE_SacarNodoFin(L);
NULL; LSE_Nodo_Eliminar(p);
return lnueva; }
} }
header
last
25. LSE: BUSQUEDA LSE_nodo *LSE_BuscarNodo(LSE L,
Generico G,
LSE_nodo *p;
Generico_fnComparar f){
Hay que ubicarse en el inicio: header for(p = L.header; p!= NULL; p = LSE_Nodo_Siguiente(p)){
if(f(LSE_Nodo_Contenido(p),G) ==0) return(p);
E ir avanzando hasta }
Encontrar el nodo con la información return(NULL);
buscada o }
Que ya no haya mas nodos
Recomendación:
Como no se usan índices, se usan punteros: Usemos el
Un puntero se ubicara en el header comportamiento
Y luego irá avanzando al siguiente, y al de 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 25
30
Se retorna la dirección de este
nodo(puntero)
header last
10 5 8 25 2 31
pp pp pp pp p p
p
26. LSE: ANTERIOR
La
posición
Dada la dirección de un nodo(pos), esta función retorna la dirección del nodo p es la
anterior
Hay que “buscar” desde el header de un
El nodo buscado es aquel cuyo siguiente es igual a pos nodo
fuera de
Si el elemento buscado es el primero en la lista, no hay anterior la lista
LSE_nodo * LSE_Anterior(LSE L, LSE_nodo *p){ p
LSE_nodo *q; Ejemplo al
if(LSE_EstaVacia(L)) return NULL; 7
if(L.header == p) return NULL; usar el
for(q=L.header; q!=NULL;q=q->sig){ estado de
if(q->sig ==p) return(q); LSE_Nodo
}
header last
return(NULL);
10 5 8 25 2 31
}
qq q q
q qp q q
q q
27. LSE: PRIMERO Y ULTIMO
Se pueden obtener siempre y cuando la lista no este vacía
Retornaran 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);
} }
28. LSE: INSERTAR
La operación de insertar recibirá
La 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
29. last->sig =
nuevo;
INSERCION AL Si la lista esta vacía, tanto header como last apuntan al
nuevo nodo
last = nuevo;
INICIO/FINAL
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
nuevo->sig = bool LSE_InsertarNodoInicio(LSE *L, bool LSE_InsertarNodoFin(LSE *L,
header; LSE_nodo *nuevo){ LSE_nodo *nuevo){
if (!nuevo) return FALSE; if(!nuevo) return FALSE;
header = if(LSE_EstaVacia(*L)){ if(LSE_EstaVacia(*L)){
nuevo; L->header = L->last = nuevo; L->header = L->last = nuevo;
} else{ } else{
LSE_Nodo_ModificarEnlace( LSE_Nodo_ModificarEnlace(L->last,,
nuevo, L->header); nuevo);
L->header = nuevo; L->last = nuevo;
} }
return(TRUE); return(TRUE);
}
}
30. INSERTAR bool LSE_Insertar(LSE *L,
LSE_nodo *p,
LSE_nodo *nuevo){
EN MEDIO
if(L->last==p){//Insertar al final
return(LSE_InsertarNodoFin(L, nuevo));
}else{
if(!p || ! LSE_ExisteNodo(*L,p))
Debe recibir la posición donde se va a return FALSE;
insertar if(LSE_EstaVacia(*L))
L->header = L->last = nuevo;
Insertemos después else{
Si Lista Vacía, el nuevo header y last, es LSE_Nodo_ModificarEnlace(
nuevo, LSE_Nodo_Siguiente(p));
el nuevo nodo
LSE_Nodo_ModificarEnlace(p, nuevo);
Si la posición no existe no efectuar la }
inserción }
Si la lista solo tiene un elemento, el nuevo return(TRUE);
last es el nuevo nodo
}
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
31. LSE: SACAR 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);
}
}
32. LSE: SACAR JUSTO UN NODO
bool LSE_EliminarxPos(LSE *L, LSE_nodo *p){
LSE_nodo *p_ant;
Lista no debe estar vacía if(LSE_EstaVacia(*L)) return 0;
if(!p || !LSE_ExisteNodo(*L, p))
La posición enviada debe existir return FALSE;
en la lista if(p==L->header)
LSE_SacarNodoInicio(L);
Revisar si se desea eliminar el else if(p == L->last)
header o el 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);
33. 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);
}