You are on page 1of 12

Facultad Ingeniería y

Tecnología Informática
Tecnicatura en Programación
de Computadoras

Estructura de
Datos: Lista

Autora:
Prof. Diana Cicinelli

Lunes a viernes de 9 a 21 h.
Torre Universitaria, Zabala 1837, primer nivel inferior.
C1426DQG - CABA
Teléfono: 4788-5400, internos 5002 y 2122.
Email: fasciculos@ub.edu.ar
www.ub.edu.ar

004300
Listas Prof. Diana Cicinelli

"La informática tiene que ver con los ordenadores


lo mismo que la astronomía con los telescopios"
Edsger W. Dijkstra

Tipos Abstractos de Datos


Lista dinámica

Introducción

Las estructuras de datos son necesarias para almacenar grandes cantidades de datos de
una forma organizada para que el procesamiento y recuperación de la información sea lo
más óptima posible.

La estructura de datos básica por excelencia es la variable, pero la dificultad que


encontramos es que almacena un dato por vez, si quisiéramos almacenar mucha más
información simultáneamente, necesitaríamos definir en nuestro programa muchas
variables, cada una con un nombre distinto, haciendo confusa sino imposible el
procesamiento y recuperación posterior de la información.

Los lenguajes de alto nivel poseen estructuras de datos "primitivas" como los arreglos para
poder almacenar muchos datos simultáneamente. En un arreglo los datos se almacenan en
secuencia en el mismo bloque de memoria y se distinguen por la posición física que
ocupan, pero también, los lenguajes nos permiten "armar" estructuras de datos más
complejas donde cada dato se almacena individualmente y se enlaza con el resto de la
información mediante direcciones de memoria.

Las estructuras de datos complejas, para que sean fáciles de manipular se deben de
implementar formando lo que se denomina un Tipo Abstracto de Datos que encapsula la
representación en memoria del dato y el conjunto de funciones para manipular la
información de manera tal que con este conjunto de funciones se defina una biblioteca de
operaciones que se pueden aplicar a los datos como si fueran funciones pertenecientes al
lenguaje. Un Tipo Abstracto de Datos permite destacar la funcionalidad o características
de la estructura de datos y ocultar al usuario la forma en la que se representa en memoria.

Dentro de los tipos abstractos de datos encontramos la Lista. Vamos a comenzar a


introducir el concepto de Lista como estructura de datos.

Clasificación de las Estructuras de Datos

Las estructuras de datos pueden ser de dos tipos según sea el tipo de almacenamiento que

1
Listas Prof. Diana Cicinelli

se use: estáticas, cuyo ejemplo básico es el arreglo y dinámicas cuyo ejemplo clásico es la
lista dinámica. En definitiva es esencial saber que tanto un arreglo como una lista
organizan la información de forma secuencial, esto quiere decir que cada elemento tiene
un antecesor y un predecesor a ecepción del primero que no tiene antecesor y el último
que no tiene sucesor.

Una lista almacenada en un arreglo estático tiene una desventaja muy importante que es
que la cantidad de datos máxima que podrá contener debe quedar fijada antes de su uso.
Cuando un programa usa arreglos debe estar sujeto a futuras modificaciones para
mofidicar el tamaño de los arreglos adecuándolos al incremento o decremento del
volumen de datos a procesar, pero también puede suceder que se reserve más memoria de
la que se necesita y por lo tanto no se use todo el espacio reservado con lo cual estamos
desperdiciando memoria que podría ser usada por otros procesos.

Una estructura de datos almacenada en forma dinámica ajusta su tamaño a las


necesidades del momento, crecerá o disminuirá su tamaño a medida que el programa se
ejecute dependiendo del volumen de datos. Una estructura así se denomina Lista
Dinámica. Una lista implementada en forma dinámica adapta su tamaño a la cantidad de
datos a procesar haciendo que el programa no se deba modificar si cambia el volumen de
datos. Esto se traduce en que cuando se ingresa un nuevo dato se solicita memoria, trámite
que lleva a cabo el compilador del lenguaje en combinación con el sistema operativo. La
solicitud de memoria se realiza en tiempo de ejecución por eso este tipo de
implementación se denomina dinámica.

Listas

Una lista es una estructura secuencial <d1, d2, d3, ..., dn>, donde n indica la cantidad de
elementos de la lista, si n = 0 se dice que la lista está vacía y se denota por <>. El primer
elemento es d1 y el último elemento es dn. Cada elemento de la lista tiene un elemento
predecesor y un elemento sucesor con excepción del primero y último, d1 no tiene
elemento anterior a él y dn no tiene un elemento posterior a él. Dado di con 1 < i < n, tiene
como predecesor a di-1 y como sucesor a di+1.

Las listas se pueden almacenar en memoria de diferentes formas, las dos formas básicas
son la estática y la dinámica. Ya mencionamos la desventanja en tener un almacenamiento
estático, por lo tanto, en este apunte vamos a tratar el almacenamiento dinámico.

Vale la pena aclarar que en el comienzo de la computación, cuando los lenguajes de alto
nivel eran muy pocos y se desconocía el almacenamiento dinámico, la única forma de
representar una lista era a través de arreglos.

Listas Dinámicas

Las listas dinámicas se clasifican en listas simplemente encadenadas y listas doblemente


encadenadas. En ambos casos el acceso a cada uno de los elementos es secuencial, esto

2
Listas Prof. Diana Cicinelli

significa que para acceder a un elemento determinado, para llegar a él hay que acceder a
todos los elementos anteriores a él, es decir que si queremos acceder a di debemos pasar
por d1, d2, d3, ..., di-1.

Una lista simplemente encadenada, enlaza cada uno de los elementos usando un puntero
mientras que una lista doblemente encadenada usa dos punteros, ambos casos se pueden
simular usando arreglos pero en este apunte vamos a usar punteros.

Lista Simplemente Encadenada

Una lista está compuesta de elementos enlazados entre sí a través de direcciones de


memoria que se denomina nodo. Cada nodo de una lista consta de dos partes: un dato, di,
y un puntero al elemento siguiente, pi, dando por resultado el par [di,pi], con 1 <= i <= n.
El último nodo tiene la característica que pi representa una dirección nula pues no hay un
elemento siguiente.

La figura siguiente representa una lista simplemente encadenada:

Un ejemplo de lista se muestra en la siguiente figura:

3
Listas Prof. Diana Cicinelli

En nuestra notación, la lista de la figura anterior es: <[2,2184],[4,3225],[6,5571],[7,NULL]>


donde, el primer elemento está almacenado en la dirección 2003 y estamos suponiendo
que el par [dato, puntero] ocupa 2 bytes.

En la figura anterior se puede apreciar que los enlaces son hacia adelante, hacia el último
elemento, este tipo de encadenamiento no permite retroceder.

TAD: Lista

Una lista como tipo abstracto de dato es una caja negra. Esto significa que tanto la forma
en la cual representemos los datos en memoria y las funciones que se definan sobre esa
representación quedan encapsuladas formando una unidad. Esta forma de trabajar con las
estructuras de datos permite escribir programas más óptimos permitiendo que el
mantenimiento del mismo sea mucho más fácil. Cuando debamos modificar la
representación de los datos para actualizarla a las necesidades del momento sabremos en
qué unidad está y lo mismo sucede con el conjunto de funciones definidas.

Las funciones hacen las operaciones definidas sobre la lista. Las operaciones que se
pueden realizar son:

• insertar un elemento nuevo


• modificar un elemento dado
• eliminar un elemento dado
• buscar un elemento
• imprimir los elementos de la lista
• esVacia (<>)

Existe otro conjunto de operaciones básicas sobre un elemento de la lista:

• obtener el dato
• obtener el puntero al siguiente
• asignar un dato
• asignar un puntero

Para completar la abstracción hacen falta dos datos más que son la dirección del primer
elemento de la lista y el elemento que está siendo procesado en un determinado momento
al que se denomina elemento actual.

Todas las operaciones salvo algunas puntuales se hacen a través de el elemento actual,
entonces podemos tener que insertar o eliminar el elemento anterior al actual o el
elemento posterior al actual, obtener el dato del elemento actual, modificar el valor del
elemento actual.

4
Listas Prof. Diana Cicinelli

Implementación en Lenguaje C

Para llevar la abstracción a la implementación en lenguaje C debemos hacer uso del tipo
struct. Usaremos el struct para encapsular los datos del nodo y de la lista.

Vamos a definir el nodo sólo con dos variables miembro, el dato y el puntero al nodo
siguiente, mientras que la lista estará definida por la dirección del primer nodo, que es la
única dirección realmente necesaria para acceder a todos los elmentos de la lista y el
elemento actual, pero podemos agregar más información útil como por ejemplo la
longitud de la lista, mantener este dato hace que sea más fácil determinar si la lista está
vacía, longitud igual a 0, y además nos evita contar los elementos de la lista cada vez que
es necesario conocer su longitud.

Para llevar a cabo la implementación vamos a hacer uso de lo que se denomina Nodo
Cabecera, este nodo no llevará información útil en sí mismo sino que se lo utilizará de
comodín para que ciertas operaciones sean más fáciles de implementar. Por ejemplo para
poder insertar un elemento nuevo en una lista se deben tener en cuenta las siguientes
posibilidades:

• insertar en una lista vacía


• insertar delante del primer elemento
• insertar entre dos elementos cualesquiera de la lista
• insertar después del último

Al tener una lista con un elemento cabecera nunca va a estar vacía físicamente, siempre
habrá un elemento que es el nodo cabecera por lo tanto nunca tendremos que insertar en
una lista vacía.

Otro elemento que vamos a incorporar es el Nodo Centinela para facilitar la operación de
eliminación cuando debemos eliminar el nodo último.

Todas las operaciones se realizarán sobre el elemento actual salvo algunas excepciones.

Vamos a tener en cuenta que los nodos centinela y cabecera permiten considerar que la
lista no está vacía desde el punto de vista físico, pero la lista está vacía desde el punto de
vista lógico porque aún no contiene información, la información por la cual estamos
construyendo la lista.

TDA Nodo
#ifndef _nodo
#define _nodo
typedef struct nodo {
tDato dato;
struct nodo * prox;
} Nodo;
#endif

5
Listas Prof. Diana Cicinelli

TDA Lista
#ifndef __ListaSimple
#define __ListaSimple
#include <stdio.h>
#include <stdlib.h>

#include "nodo.h"
typedef Nodo * Actual;

typedef struct {
Nodo * com;
Nodo * actual;
int longitud;
}Lista;

void crearLista (Lista * l){


Nodo * p = (Nodo *) malloc (sizeof(Nodo)); //nodo cabecera
Nodo * q = (Nodo *) malloc (sizeof(Nodo)); //nodo centinela
p->prox = q; //el nodo cabecera apunta al nodo centinela cuando la
//lista está vacía
q->prox = NULL;
l->longitud = 0;
l->com = p; //el primer nodo es el nodo cabecera
l->actual = p; //el elemento actual es el nodo cabecera cuando la
// lista está vacía
}

//cambiar el contenido del actual


void setDato (Lista * l, tDato t)
{
l->actual->dato = t;
}

//obtener el dato del actual


tDato verActual (Lista *l){
return l->actual->dato;
}

//verificar el estado actual de la lista


int esListaVacia (Lista *l){
return l->longitud == 0;
}

//verificar si el elemento actual es el primero de la lista lógica


int esPrimero (Lista * l){
return l->actual == l->com->prox;
}

//asigna al elemento actual la dirección del primer elemento lógico


void primero (Lista * l){
if (!esPrimero(l))
l->actual = l->com->prox;
}

//verifica que el elemento actual apunte al nodo centinela


int esUltimo (Lista * l){
return l->actual->prox == NULL;}

6
Listas Prof. Diana Cicinelli

//avanza al siguiente elemento de la lista a partir del elemento actual


void siguiente (Lista * l){
if (!esUltimo(l))
l->actual = l->actual->prox;
}

//recorre la lista mostrando la información almacenada en cada nodo


void mostrarLista (Lista * l){
primero(l);
while(!esUltimo(l)){
mostrarDato(verActual(l)); //mostrarDato corresponde a la
//abstracción de la
//información almacenada en el nodo
siguiente(l);
}
printf("\n");
}

//inserta el dato d después del elemento actual


void insertarDespues (Lista * l, tDato d){
Nodo * p = (Nodo *) malloc (sizeof(Nodo)); //pide memoria para el nuevo
// dato
p->dato = d;
//actualiza los punteros para que el nodo p quede enlazado entre el actual y
// el próximo
p->prox = l->actual->prox;
l->actual->prox = p;
l->actual = p;
(l->longitud)++; //incrementa en 1 el tamaño de la lista
}

//inserta el nuevo dato d antes del elemento actual


void insertarAntes (Lista *l, tDato d){
Nodo * p ;
if (l->longitud > 0){
p = (Nodo *) malloc (sizeof(Nodo));
p->prox = l->actual->prox;
l->actual->prox = p;
p->dato = l->actual->dato;
l->actual->dato = d;
}
else
insertarDespues(l, d); //si la lista está vacía inserta después del
//nodo cabecera
(l->longitud)++;
}

//elimina el nodo actual


void eliminarActual (Lista *l){
Nodo * p = l->actual->prox;
(l->longitud)--; //actualiza la longitud de la lista decrementando en 1
// el campo longitud
l->actual->dato = l->actual->prox->dato;
l->actual->prox = l->actual->prox->prox;
free(p);
}

7
Listas Prof. Diana Cicinelli

//recorre la lista buscando el dato d retornando verdadero o falso


int buscarDato (Lista *l, tDato d){
tDato e;
int encontrado = 0;
if (!esListaVacia(l))
{
primero (l);
while (!esUltimo(l)){
e = verActual(l);
if (compara(e, d) == 0){ //la función compara es necesaria
//dependiendo de la complejidad del
// dato almacenado en la lista
encontrado = 1;
break;
}
siguiente (l);
}
}
return encontrado;
}

//obtiene la dirección del elemento actual


Actual getActual(Lista * l){
return l->actual;
}

//obtiene la dirección del siguiente dato al actual en la lista


Actual getSiguiente (Lista * l){
return l->actual->prox;
}

//obtiene la longitud de la lista


int getLongitud (Lista *l){
return l->longitud;
}

//el elemento actual se ubica en el k-ésimo elemento de la lista


void posicionar (Lista *l, int k){
int i=1;
if (l->longitud > = k){
primero (l);
while (i<k)
{
siguiente (l);
i++;
}
}
}

//sitúa el elemento actual en el anterior a él


void anterior (Lista *l){
Nodo * p = l->actual;
primero(l);
while (getSiguiente(l) != p){
siguiente(l);
}
}

8
Listas Prof. Diana Cicinelli

//convierte la lista en un archivo almacenando los datos en un archivo


void listaAArchivo (Lista *l, char *arch)
{
tDato e;
FILE *fp;
if((fp=fopen(arch,"wb"))==NULL)
{
printf("\n\n Error, no se puede abrir el archivo: %s\n",arch);
exit(0);
}
primero(l);
while(!esUltimo(l))
{
e=verActual(l);
fwrite(&e,sizeof(tDato),1,fp);
siguiente(l);
}
fclose(fp);
}

//vacía la lista, eliminando cada nodo de ella sin eliminar los nodos cabecera y
centinela
void vaciarLista(Lista *l)
{
int i;
primero(l);
while (!esUltimo(l))
{
eliminarActual(l);
}
l->actual = l->com;
}

//Crea una lista a partir de los datos almacenados en un archivo


void archivoALista(Lista *l, char *arch)
{
tDato e;
FILE *fp;
vaciarLista(l);
if((fp=fopen(arch,"rb"))==NULL)
{
printf("\n\n Error, no existe el archivo: %s\n",arch);
exit(0);
}
while(fread(&e,sizeof(tDato),1,fp)==1)
{
insertarDespues(l,e);
}
fclose(fp);
}

//destruye la lista vaciándola y destruyendo el nodo cabecera y el nodo


centinela
void destruir(Lista *l)
{
vaciarLista(l);
free(l->com->prox);
free(l->com);
}

9
Listas Prof. Diana Cicinelli

//crea una lista cuyos datos están ordenados de menor a mayor


void insertarEnOrden(Lista *l, tDato d)
{
if(esListaVacia(l))
insertarDespues(l,d);
else
{
primero(l);
while(!esUltimo(l))
{
if(compara(d,verActual(l))>0)
siguiente(l);
else
break;
}
insertarAntes(l,d);
}
}

//elimina el elemento de la posición k


void eliminarPos (Lista *l, int k)
{
posicionar(l, k);
eliminarActual(l);
}

//inserta un nuevo dato en la posición k


void insertarEn (Lista *l, tDato d, int k)
{
posicionar(l, k-1);
insertarDespues(l, d);

}
#endif

 

El hombre tiene mil planes para sí mismo.


El azar, sólo uno para cada uno.
Mencio o Meng Zi, filósofo chino confucciano

10
Facultad Ingeniería y
Tecnología Informática
Tecnicatura en Programación
de Computadoras

Estructura de
Datos: Lista

Autora:
Prof. Diana Cicinelli

Lunes a viernes de 9 a 21 h.
Torre Universitaria, Zabala 1837, primer nivel inferior.
C1426DQG - CABA
Teléfono: 4788-5400, internos 5002 y 2122.
Email: fasciculos@ub.edu.ar
www.ub.edu.ar

004300

You might also like