Albert)
LISTAS
13
13.1. Introduccin
Las listas son secuencias de elementos, a1, a2, a3,... ,an-1, an, en las que las operaciones de
manipulacin pueden tener lugar en cualquier posicin de la secuencia.
En una lista, como en toda secuencia, diremos que el elementos ai+1 sigue o sucede al
elemento ai (si i < n), y diremos que el elemento ai-1 precede o es anterior a ai (si i > 1).
Las operaciones propias de las listas son las habituales de cualquier contenedor de
informacin: Crear la lista vaca; aadir elementos a la lista (en cualquier posicin); eliminar
elementos (de cualquier posicin). Aunque se pueden especificar otras segn las necesidades
de la aplicacin, como buscar un determinado elemento o mostrar todos los elementos de la
lista.
Si nos damos cuenta las operaciones que se definen nos relacionan claramente las listas con
las pilas y las colas, siendo las listas generalizaciones de estas (las operaciones de insercin y
borrado pueden realizarse en cualquier punto de la secuencia y no slo sobre los extremos de
la misma.)
Pg. 1
Tema 13. Listas
Especificacin informal
INICIAR_LISTA ( ) Lista
Inicia la lista a vaca, con solo el punto final de la lista y el punto de inters
sobre este final de la lista
list *
Pg. 2
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
list *
ai-1
a1 a2 ... ai ai+1 ... an
Lista_Vacia (list) false
Pg. 3
Tema 13. Listas
Pg. 4
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
Algunos axiomas
list Lista, x Valor se cumple que:
LISTA_VACIA ( INICIAR_LISTA ( ) ) cierto
LISTA_VACIA ( INSERTAR ( list, x ) ) falso
ELIMINAR ( INICIAR_LISTA ( ) ) error
Si CONSULTAR ( list ) = x entonces
INSERTAR ( ELIMINAR ( list ), x ) list
FINAL_LISTA ( INICIAR_LISTA ( ) ) cierto
FINAL_LISTA (INSERTAR (list, x) FINAL_LISTA(list)
AVANZAR ( INICIAR_LISTA ( ) ) error
...
Una vez vistas las operaciones, su comportamiento en la descripcin informal y algunos de
los axiomas que las rigen, ya podemos plantearnos la utilizacin del tipo abstracto de datos
Lista.
Ejemplo de utilizacin de listas:
1. Realizar una funcin que busque un determinado valor en una lista.
encontrado falso
list Ir_A_Inicio (list)
Mientras ( no Final_Lista (list) y no encontrado):
Si ( Consultar (list) = y ) Entonces
encontrado cierto
Sino
list Avanzar (list)
Fin_si
Fin_mientras
Devolver (encontrado)
Fin
Pg. 5
Tema 13. Listas
2. Realizar una funcin que obtenga la media de los elementos contenidos en una
lista, suponiendo el tipo Valor como Real.
acc 0
num 0
Si (num = 0)
med 0
Sino
med acc / num
Fin_si
Devolver (med)
Fin
Ejercicio:
3. Realizar un programa en pseudocdigo que pida valores enteros al usuario y los
inserte ordenadamente en una lista de enteros (tras insertar, por ejemplo 4, 3, 6, 5, 1
la lista contendr los valores en el orden correcto, es decir, 1, 3, 4, 5, 6.) El proceso
se detendr cuando introduzcamos un valor negativo.
Pg. 6
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
class Lista
{
public:
typedef .??. Valor;
Lista (void);
Lista (const Lista &);
~Lista (void);
cons Lista& operator= (const Lista &);
void Insertar (Valor);
bool Eliminar (void);
bool Consultar (Valor &);
bool ListaVacia (void);
private:
????
};
encontrado = false;
list.IrAInicio ( );
while ( !list.FinalLista ( ) && !encontrado )
{
list.Consultar (x);
if (x == y)
encontrado = true;
else
list.Avanzar ( );
}
return encontrado;
}
Pg. 7
Tema 13. Listas
2. Realizar una funcin que obtenga la media de los elementos contenidos en una
lista, suponiendo el tipo Valor como Real.
float Media (Lista list)
{
Lista::Valor x;
float acc, med;
int num;
acc = 0;
num = 0;
list.IrAInicio ( );
while ( !list.FinalLista () )
{
list.Consultar (x)
acc = acc + x;
num = num + 1;
list.Avanzar ( )
}
if (num == 0)
med = 0;
else
med = acc / num;
return med;
}
Tambin es posible plantear esta funcin utilizando una estructura de bucle for de
C++ controlado por las operaciones de manipulacin del punto de inters de la
siguiente manera:
float Media (Lista list)
{
Lista::Valor x;
float acc, med;
int num;
acc = 0;
num = 0;
for ( list.IrAInicio(); !list.FinalLista(); list.Avanzar() )
{
list.Consultar (x)
acc = acc + x;
num = num + 1;
}
if (num == 0)
med = 0;
else
med = acc / num;
return med;
}
Pg. 8
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
private:
typedef Valor Vector[MAX];
Vector info;
int fin;
int pto;
};
MAX es una constante que marca la capacidad mxima de la lista y Valor el tipo de
informacin que puede contener la lista.
Con esta definicin de la parte privada de la lista podemos plantearnos la implementacin de
los mtodos que constituyen las clase.
Para tener una idea clara de los problemas planteados por los arrays en este caso,
comenzaremos por la operacin de insercin.
La insercin de un nuevo elemento requiere aadir el elemento delante del punto de inters,
sto implica abrir un hueco delante de la posicin del punto de inters e insertar la
informacin. sto se consigue desplazando una posicin a la derecha todos los elementos
situados desde la posicin del punto de inters hasta el final de las lista.
if (fin == MAX)
cerr << Error, lista llena. << endl;
else
{
error = true;
info[pto] = x;
fin++;
}
}
Pg. 9
Tema 13. Listas
Esta operacin tiene un coste cuya dependencia es claramente lineal respecto al nmero de
elementos de la lista. Mayor es el nmero de elementos, mayor es el nmero esperado de
desplazamientos que ser preciso realizar para aadir un elemento.
El mismo problema de desplazamiento de informacin se presenta cuando es preciso eliminar
un elemento. En este caso, es preciso tapar el hueco dejado por elemento eliminado,
desplazando todos los elementos posteriores al punto de inters una posicin hacia la
izquierda.
El elevado coste de las operaciones fundamentales del tipo lista al representarla mediante un
array descarta la utilizacin prctica de esta forma de implementacin. Por ese motivo,
pasamos directamente a tratar el tema de la implementacin dinmica.
private:
struct Nodo;
typedef Nodo* Puntero;
struct Nodo
{
Valor info;
Puntero sig;
};
Con est representacin los mtodos de la clase Lista quedaran como sigue:
Pg. 10
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
Insercin delante del punto de inters
En principio, para insertar delante del punto de inters hay que conocer el elemento anterior al
que seale el punto de inters. Primero haremos una bsqueda de ese elemento (si existe) y a
partir de este elemento insertaremos. En este caso supondremos que siempre se puede reservar
memoria para el nuevo elemento.
void Lista::Insertar (Valor x)
{
Puntero p_aux, p_aux2;
p_aux = new Nodo;
p_aux->info = x;
p_aux->sig = pto;
/* Si la insercin es delante de la cabeza */
if (pto == ini)
{
ini = p_aux;
}
/* En cualquier otro caso */
else
{
p_aux2 = ini;
while (p_aux2->sig != pto)
p_aux2 = p_aux2->sig;
p_aux2->sig = p_aux;
}
}
Esta insercin, en general, tiene un coste que depende linealmente con el nmero de
elementos contenidos en la lista, debido al recorrido que se hace desde el inicio de la lista
hasta el elemento anterior al punto de inters. La eficiencia de la implementacin se puede
mejorar notablemente haciendo una pequea trampa, que consiste bsicamente en insertar el
nuevo nodo detrs del punto de inters, copiar en este nuevo nodo la informacin del punto
de inters y, por ltimo, modificar la informacin contenida en el punto de inters con el
nuevo dato que hay que insertar:
Pg. 11
Tema 13. Listas
Pg. 12
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
bool Lista::Eliminar ()
{
Puntero aux;
bool ok;
Pg. 13
Tema 13. Listas
if ( pto == NULL )
ok = false;
else
{
ok = true;
x = pto->Info;
}
return (ok);
}
Lista Vaca
Comprueba si en la lista existe hay algn elemento.
bool Lista::ListaVacia (void)
{
return (ini == NULL);
}
Pg. 14
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
if (pto == NULL)
ok = false; //no se puede avanzar
else
{
ok = true;
pto = pto->sig;
}
return (ok);
}
Pg. 15
Tema 13. Listas
aux = orig.ini;
dup = NULL;
/* se recorre toda la lista orig */
while ( aux != NULL )
{
dup = new Nodo; //se crea un nuevo Nodo en cada iteracin
dup->info = aux->info; //copia la informacin de orig
dup->sig = NULL; //de momento, ste es el ltimo elemento
if (ini == NULL) //si la copia est vacia, fijar su inicio
ini = dup;
else //detras del fin
fin->sig = dup;
fin = dup; //siempre es el fin
if ( aux == orig.pto ) //fijar pto. de interes
pto = dup;
aux = aux->sig;
}
}
La operacin Vaciar debe liberar la memoria de todos los elemento de la lista. Esto se puede
realizar fcilmente sin ms que llamar repetidamente a la operacin que elimina un elemento
de la lista. Para ello, es preciso ubicar el punto de inters al inicio de la lista e ir eliminando
mientras haya elementos.
void Lista::Vaciar ()
{
pto = ini;
while ( Eliminar() );
}
Una vez definidas las dos operaciones privadas resulta inmediato construir las tres
operaciones pblicas:
Lista::Lista (const Lista& orig)
{
Copiar (orig);
}
Lista::~Lista ()
{
Vaciar();
}
Pg. 16
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
Pg. 17
Tema 13. Listas
Como se puede ver todas las operaciones tienen una solucin conceptualmente simple y, a
nivel de coste temporal, el comportamiento de casi todas es bueno. No obstante, la desventaja
reside en las operaciones que mueven el punto de inters. Estas operaciones tienen que
desplazar elementos de una pila a otra. Este proceso es notablemente ms ineficiente que el
realizado utilizando una representacin con punteros, sobre todo en el caso de llevar el punto
de inters al inicio de la lista.
Pg. 18
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
pto
ini fin
24
struct NodoDoble;
typedef PunteroDoble;
struct NodoDoble
{
Valor info;
PunteroDoble ant;
PunteroDoble sig;
};
PunteroDoble ini, fin, pto;
Listas circulares
Otra posible variante que se puede incluir en la implementacin es la conversin de la lista en
circular, es decir, hacer que el siguiente elemento del ltimo de la lista vuelva a ser el primero
(y el anterior del primero el ltimo). Con esto se consigue que no exista el puntero a NULL al
final de la lista y evitar el caso particular en que el punto de inters apunta a un elemento no
vlido o el caso en el que no existe un elemento detrs del ltimo.
Pg. 19
Tema 13. Listas
pto
pto
cabeza
? 32 7 24 86 25
private:
struct Nodo;
typedef Nodo* Puntero;
struct Nodo
{
Valor info;
Puntero sig;
Puntero ant;
};
Pg. 20
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
/*
* Constructor por defecto
*/
Lista::Lista (void)
{
cab = new Nodo;
cab->sig = cab;
cab->ant = cab;
}
/*
* Constructor de copia
*/
Lista::Lista (const Lista & ori)
{
/* inicia la lista como vaca */
/* y despus copia */
cab = new Nodo;
cab->sig = cab;
cab->ant = cab;
Copiar (ori) ;
}
/*
* Destructor de la clase
*/
Lista::~Lista (void)
{
Vaciar();
}
/*
* Operador asignacin
*/
const Lista & Lista::operador= (const Lista & ori)
{
Copiar (orig);
Return (*this);
}
Pg. 21
Tema 13. Listas
/*
* Insercin de nuevos elementos delante del punto de inters
*/
void Lista::Insertar (Valor x)
{
q_aux = new Nodo;
q_aux->info = x;
q_aux->sig = pto;
q_aux->ant = pto->ant;
pto->ant->sig = q_aux;
pto->ant = q_aux;
}
/*
* Eliminacin del elemento apuntado por el punto de inters
*/
bool Lista::Eliminar (void)
{
bool ok;
if (pto == cab)
ok = false;
else
{
ok = true;
p_aux = pto->sig;
pto->sig->ant = pto->ant;
pto->ant->sig = pto->sig;
delete pto;
pto = p_aux;
}
return ok;
}
/*
* Consulta del elemento apuntado por el punto de inters
*/
bool Lista::Consultar (Valor & x)
{
bool ok;
if (pto == cab)
ok = false;
else
{
ok = true;
x = pto->info;
}
return ok;
}
Pg. 22
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)
/*
* Comprobacin de si existen elementos en la lista adems de la
cabeza
*/
bool Lista::ListaVacia (void)
{
return (cab == cab->sig);
}
/*
* Situar el punto de inters en el primer elemento vlido de la
lista
*/
void Lista::IrAInicio (void)
{
pto = cab->sig;
}
/*
* Avanzar el punto de inters si no hemos llegado al final de la
lista
*/
bool Lista::Avanzar (void)
{
bool ok;
if (pto == cab)
ok = false;
else
{
ok = true;
pto = pto -> sig;
}
return (ok);
}
/*
* Comprobar que hemos llegado al final de la lista
* (comprobar que de nuevo estamos en la cabeza)
*/
bool Lista::FinalLista (void)
{
return pto == cab;
}
Pg. 23
Tema 13. Listas
/*
* Copiar la lista ori en this
*/
void Lista::Copiar (const Lista & ori)
{
Puntero p_aux, q_aux;
/*
* Y se van aadiendo los elementos delante de la cabeza
* es decir al final de la lista
*/
q_aux->sig = cab;
q_aux->ant = cab->ant;
cab->ant->sig = q_aux;
cab->ant = q_aux;
/*
* Si estamos copiando el pto de inters lo fijamos en
* la copia
*/
if (p_aux == ori.pto)
pto = q_aux;
p_aux = p_aux->sig;
}
}
/*
* Vaciar la lista
*/
void Lista::Vaciar (void)
{
IrAInicio();
while (Eliminar());
}
Pg. 24