Está en la página 1de 33

TDA LISTA

ESTRUCTURAS DE DATOS
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
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
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
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
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>
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>
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 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
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);
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
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){ }
Si la lista está llena es
ArrayU_Eliminar(&(L->Elementos));
porque el índice del último
LSCont_VaciarLista(L); ha llegado al verdadero
último índice posible en el
} arreglo: MAX -1
CONTIGUA: BUSQUEDA
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 bool 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;
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);
ArrayU_ElemM(L->Elementos,i+1, ele1);
//No insertar si ya esta 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 1
5 8
5 8
}
0 1 2 3 4 5 6 7 8 9
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 Eliminar por Info, dada
una cierta información,
for(i = P; i < L->ultimo;i++){ buscar el elemento que
ele1 = ArrayU_ElemC(L->Elementos, i+1); la tenga y eliminarlo
ArrayU_ElemM(L->Elementos, i, ele1);
}
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 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 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
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
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, 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;
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;
}
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
 bool LSE_EstaVacia(LSE L);
 Añadir y Remover
 bool LSE_InsertarNodoInicio(LSE *L, LSE_nodo *pNodo);
 bool LSE_InsertarNodoFin(LSE *L, LSE_nodo *pNodo);
 bool 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);
 bool 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
typedef LSE_Nodo * LSE;

 O llevando control del primero y el último elemento


typedef struct{
LSE_Nodo *header;
LSE_Nodo *last;
}LSE;
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
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 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
La
posición
 Dada la dirección de un nodo(pos), esta función retorna la dirección del nodo panterior
es la
 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 qq qp q q
q q
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);
} }
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
last->sig =
nuevo;

last = nuevo;

INSERCION AL INICIO/FINAL
Si la lista esta vacía, tanto header como last apuntan al
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

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);
}
}
INSERTAR bool LSE_Insertar(LSE *L,
LSE_nodo *p,
LSE_nodo *nuevo){
if(L->last==p){//Insertar al final

EN MEDIO 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(
el nuevo nodo nuevo, LSE_Nodo_Siguiente(p));
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
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);
}
}
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;
 La posición enviada debe existir en la lista if(!p || !LSE_ExisteNodo(*L, p))
 Revisar si se desea eliminar el header o el return FALSE;
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);

También podría gustarte