You are on page 1of 24

Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J.

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.)

13.2. Listas con punto de inters


Existen diferentes modelos de uso e implementacin de las listas y uno de los ms
interesantes y extendidos son las llamadas listas con punto de inters. El objetivo de este
planteamiento es ocultar al usuario del tipo la informacin sobre la posicin en la que se
llevan a cabo las operaciones. Ya que este tipo de informacin puede estar directamente
relacionada con la forma de implementacin (un nmero entero si se implmeneta con un
array, un puntero si se trabaja con estructuras dinmicas, etc).
En las listas con punto de inters se define la existencia de un determinado elemento
destacado que servir de referencia para la realizacin de todas las operaciones. Este
elemento destacado puede modificarse con la aplicacin de otras funciones, y vendr marcado
como punto de inters de la lista (lo sealaremos con un * en los ejemplos). El punto de
inters puede referenciar a cualquier elemento de la lista o situarse a continuacin del ltimo,
fuera de la secuencia. Esta ltima ubicacin del punto de inters servir para realizar
operaciones al final de la lista, y diremos en este caso que el punto de inters est totalmente
a la derecha de la lista. A ese punto le llamaremos desde este momento Final de la lista (y
lo dibujaremos como .) Sin embargo no se podr situar el punto de inters por delante del
primer elemento de la lista, a lo sumo sobre l.
Las operaciones que vamos a definir sobre las listas con punto de inters, sern similares a las
vistas ya con pilas y colas: Crear la estructura vaca, aadir informacin a la estructura,
eliminar informacin de la estructura, consultar informacin de la estructura y verificar la
existencia de informacin en la estructura. Pero como en este caso las operaciones no se
realizan slo en uno de los extremos de la estructura o en puntos concretos de la estructura,
habr que aadir operaciones especificas para elegir el punto de la estructura en donde
realizar las operaciones (operaciones para mover el punto de inters de la lista.)

Pg. 1
Tema 13. Listas

En adelante, y en todo el tema, siempre que hablemos de Listas estaremos refirindonos a


listas con punto de inters. Veamos ahora la especificacin del tipo abstracto:
Estructura
Lista ( Valor ){* Valor ser el tipo de datos que podremos guardar en la lista
*}
Operaciones
/* Operaciones similares a las vistas en Pilas y Colas */
INICIAR_LISTA ( ) Lista
INSERTAR ( Lista , Valor ) Lista
ELIMINAR ( Lista ) Lista
CONSULTA ( Lista ) Valor
LISTA_VACIA ( Lista ) Lgico
/* Operaciones especficas */
IR_A_INICIO ( Lista ) Lista
AVANZAR ( Lista ) Lista
FINAL_LISTA ( Lista ) Lgico

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 *

INSERTAR ( Lista , Valor ) Lista


Inserta el Valor delante de elemento sealado como punto de inters, dejando
el punto de inters sobre el elemento en el que estaba.
list *

a1 a2 ... ai-1 ai ai+1 ... an
Insertar (list, x)
List *

a1 a2 ... ai-1 x ai ai+1 ... an

Pg. 2
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)

ELIMINAR ( Lista ) Lista


Modifica la lista eliminando el elemento sealado por el punto de inters,
dejando el punto de inters sobre el elemento siguiente al eliminado. Si el
punto de inters sealaba el final de la lista devolver error.
list *

ai-1
a1 a2 ... ai ai+1 ... an
Eliminar (list)
list *

ai-1
a1 a2 ... ai+1 ... an

CONSULTAR ( Lista ) Valor


Devuelve la informacin sealada por el punto de inters. Si el punto de
inters sealaba el final de la lista devolver error.
list *

ai-1
a1 a2 ... ai ai+1 ... an
Consultar (list) ai

LISTA_VACIA ( Lista ) Lgico


Nos informa si existe informacin en la lista o no (nos dice si el nmero de
elementos de la lista es cero o diferente de cero.)
list *


Lista_Vacia (list) true

list *

ai-1
a1 a2 ... ai ai+1 ... an
Lista_Vacia (list) false

Pg. 3
Tema 13. Listas

IR_A_INICIO ( Lista ) Lista


Mueve el punto de inters al primer elemento de la lista.
list *

ai-1
a1 a2 ... ai ai+1 ... an
Ir_A_Inicio (list)
list *

ai-1
a1 a2 ... ai ai+1 ... an

AVANZAR ( Lista ) Lista


Avanza el punto de inters al siguiente elemento de la lista.
list *

ai-1
a1 a2 ... ai ai+1 ... an
Avanzar (list)
list *

ai-1
a1 a2 ... ai ai+1 ... an
FINAL_LISTA ( Lista ) Lgico
Nos dice si el punto de inters est o no al final de la lista.
List *

ai-1
a1 a2 ... ai ai+1 ... an
Final_Lista (list) false
List *

ai-1
a1 a2 ... ai ai+1 ... an
Final_Lista (list) true

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.

Funcion Buscar (Lista list, Valor y): Boolean


Inicio
Booleano encontrado;

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.

Funcion Media (Lista list): Real


Inicio
Real acc, med;
Entero num;

acc 0
num 0

list Ir_A_Inicio (list)


Mientras ( no Final_Lista (list) ) Hacer
acc acc + Consultar (list)
num num + 1
list Avanzar (list)
Fin_mientras

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.

13.3. Implementacin de la clase LISTA en C++


Al igual que hicimos en los temas de Pilas y Colas, la implementacin en C++ la haremos
utilizando clases.
Recordar que la iniciacin de la estructura a vaca, se la dejaremos al constructor de la clase, y
que, al igual que hicimos en Pilas y Colas, las funciones que puedan producir errores
devolvern booleanos que indicarn si la operacin se ha producido satisfactoriamente o no.

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);

void IrAInicio (void);


bool Avanzar (void);
bool FinalLista (void);

private:
????
};

La especificacin de la parte privada la dejaremos para un poco ms adelante, en el momento


en que especifiquemos qu implementacin utilizaremos de la Lista: Una implementacin
esttica (utilizacin de vectores para guardar la informacin) o una dinmica (mediante la
utilizacin de punteros para crear una estructura enlazada).
Con esta especificacin ya estamos en condiciones de escribir programas o funciones que
hagan uso de esta parte pblica y trabajen con Listas.
Ejemplo de utilizacin de listas con el interfaz propuesto en C++:
1. Realizar una funcin que busque un determinado valor en una lista.
bool Buscar (Lista list, Lista::Valor y)
{
bool encontrado;
Lista::Valor x;

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)

13.3.1 Implementacin de la clase LISTA en C++ con vectores


En primer lugar implementaremos las listas utilizando como tipo base un vector. Para guardar
la informacin contenida en la lista, utilizaremos el vector, pero tambin necesitaremos
guardar la informacin de cuntos elementos contiene la lista. La manera en que guardaremos
la informacin ser compactando los elementos en las primeras posiciones del vector, de
manera que la lista siempre comenzar en la posicin cero.
Con estas consideraciones, y sabiendo que necesitamos una marca para localizar el elemento
destacado, la parte privada de la clase quedara como sigue:
class Lista
{
public:
...

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.

void Lista::Insertar (x)


{
int i;

if (fin == MAX)
cerr << Error, lista llena. << endl;
else
{
error = true;

for (i = fin; i >= pto; i--)


info[i+1] = info[i];

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.

13.3.2 Implementacin de la clase LISTA en C++ con punteros


En principio tendramos que una lista queda determinada por el puntero que marca su inicio y
el puntero que marca el elemento destacado o punto de inters. En general, tambin es
interesante marcar el ltimo de los elementos. De manera que necesitaremos tres punteros
para determinar exactamente la Lista. La parte privada de la clase quedara de la siguiente
manera:
class Lista
{
public:
...

private:
struct Nodo;
typedef Nodo* Puntero;
struct Nodo
{
Valor info;
Puntero sig;
};

Puntero ini; /* Puntero al inicio de la lista */


Puntero fin; /* Puntero al inicio de la lista */
Puntero pto; /* Punto de inters */
};

Con est representacin los mtodos de la clase Lista quedaran como sigue:

Iniciacin: Constructor de la clase


Los objetos de la clase quedan iniciados poniendo el inicio de la lista a Null y el punto de
inters en ese elemento.
Lista::Lista ()
{
ini = NULL;
fin = NULL; // Realmente no es necesaria esta iniciacin
pto = NULL;
}

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

void Lista::Insertar ( Valor x )


{
Puntero aux;

aux = new Nodo;


/* si el pto est ms all del final de la lista
if (pto == NULL)
{
aux->info = x;
aux->sig = NULL;
if (ini == NULL) //si lista vacia
ini = aux;
else //ubicar detras del fin
fin->sig = aux;
fin = aux; //el nuevo siempre es el fin
}
/* En cualquier otro caso */
else
{
*aux = *pto; //copiar datos del pto en el nuevo nodo
pto->info = x; //cambiar datos del pto
pto->sig = aux;
if ( pto == fin ) //caso particular, trasladar fin
fin = aux;
pto = aux; //el pto sigue apuntando a la misma informacin
}
}

Eliminacin del dato que hay en el punto de inters


Al igual que en la insercin, para eliminar el punto de inters hay que localizar el elemento
anterior al punto de inters. Una vez hecho esto, modificar los enlaces, modificar el punto de
inters y liberar el espacio ocupado. No obstante, como acabamos de ver que retroceder al
elemento anterior puede ser muy costoso y vamos a plantear una implementacin de la
operacin eliminar que acte de manera similar a como lo hemos hecho en la operacin
insertar: en realidad va a desaparecer el nodo que est detrs del punto de inters. Para ello, es
preciso copiar la informacin de dicho nodo (que se debe mantener en la lista) en el punto de
inters. De manera que, la informacin que tena el punto de inters desaparece, que es lo que
se deseaba.

Pg. 12
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)

bool Lista::Eliminar ()
{
Puntero aux;
bool ok;

/* error si el pto est ms all del final de la lista */


/* esto ocurre tambin cuando la lista est vaca */
if ( (pto == NULL) )
ok = false;
/* en cualquier otro caso, se puede eliminar */
else
{
ok = true;
/* caso en que pto es el ltimo elemento y no hay nada detrs */
if (pto == fin)
{
/* adems de ser el ltimo tambin es nico elemento */
if (pto == ini)
{
delete pto;
ini = fin = pto = NULL;
}
else
{
aux = ini;
//hay que buscar el anterior a fin
while ( aux->sig != fin )
aux = aux->sig;
fin = aux;
fin->sig = NULL;
delete pto;
pto = NULL;
}
}
/* caso en que pto NO es el ltimo elemento */
else
{
aux = pto->sig; //guardar el nodo que va a desaparecer
*pto = *aux; //copiar los datos del nodo en pto
if (aux == fin) //si desaparece el ltimo cambiar fin
fin = pto;
delete aux; //liberar memoria
}
}
return (ok);
}

Pg. 13
Tema 13. Listas

Consulta del valor almacenado en el punto de inters


Se limitar a devolver el valor contenido en el elemento destacado (pto), si ste apunta a un
elemento vlido de la lista, es decir, si no est ms all del final de la lista.
bool Lista::Consultar (Valor & x)
{
bool ok;

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);
}

Mover al inicio el punto de inters


Pone el punto de inters en el primer elemento de la lista.
void Lista::IrAInicio (void)
{
pto = ini;
}

Avanzar el punto de inters


Avanza el punto de inters al siguiente elemento de la lista si el punto de inters no estaba ya
en el punto ms a la derecha de la lista.

Pg. 14
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)

bool Lista::Avanzar (void)


{
bool ok;

if (pto == NULL)
ok = false; //no se puede avanzar
else
{
ok = true;
pto = pto->sig;
}
return (ok);
}

Comprobar si el punto de inters est al final de la lista


Nos dice si el punto de inters est sobre el punto ms a la derecha de la lista (ha sobrepasado
el ltimo elemento de la lista.)

bool Lista::FinalLista (void)


{
return (pto == NULL);
}

El constructor de copia, destructor y operador asignacin


Como ya sabemos, estas tres operaciones son necesarias para el correcto funcionamiento de
una clase implementada mediante estructuras dinmicas (punteros). El planteamiento bsico
va a ser el mismo que se utiliz en pilas y colas, implementar dos operaciones de carcter
privado que sern las que realicen de manera efectiva la copia de una lista en otras (Copiar) y
la eliminacin y liberacin de memoria de todos los elementos almacenados en una lista
(Vaciar).
La operacin Copiar copia en la lista del objeto receptor la lista original pasada por
referencia. Ser similar a la operacin realizada para pilas y colas, pero teniendo en cuenta
que tambin hay que copiar el punto de inters.

Pg. 15
Tema 13. Listas

void Lista::Copiar (const Lista& orig)


{
Puntero aux, dup;

Vaciar(); //asegura que la lista sobre la que se copia est vaca

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();
}

const Lista& Lista::operator= (const Lista& orig)


{
Copiar (orig);
return (*this);
}

Pg. 16
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)

13.3.3 Implementacin de la clase LISTA en C++ con pilas


Para ver que las implementaciones de cualquier tipo de datos pueden ser muy diversas,
analizaremos otra posible implementacin de las listas con punto de inters. En este caso,
mediante la utilizacin de otro tipo abstracto de datos, el tipo Pila.
Podemos ver una lista con punto de inters como dos pilas enfrentadas. Una que contiene los
elementos situados delante del punto de inters y otra con el resto de elementos (incluido el
punto de inters). De manera que, el punto de inters siempre est situado en la cima de esta
ltima pila.
Por ejemplo, sea la siguiente lista:
list *

a b c d e f g h i j k l

Es posible visualizarla como dos pilas de esta forma:


Veamos, con esta filosofa como se
pila_izq pila_der implementaran cada una de las operaciones
f pblicas de la clase:
g
Lista (void);
e h Lista (const Lista &);
~Lista (void);
d i
c j void Insertar (Valor x);
bool Eliminar (void);
b k bool Consulta (Valor & x);
bool ListaVacia (void);
a l
void IrAInicio (void);
bool Avanzar (void);
bool FinalLista (void);

Respecto a los constructores y el destructor de la clase:


Constructor por defecto: Como la clase contiene en su parte privada dos pilas, el hecho de
declarar una lista supone declarar las dos pilas, que tendrn asociado su propio constructor
por defecto.
Constructor de copia: Al pasar una lista por valor, sta contendr una copia de las dos
pilas, que llamarn a sus propios constructores de copia si es necesario.
Destructor de la clase: Al acabar la vida del objeto Lista, acabar la vida de las pilas,
que llamarn a su propio destructor si es necesario.
En conclusin, tanto los constructores de la clase, como el destructor no necesitan
realizar ninguna tarea.

Pg. 17
Tema 13. Listas

Respecto de la informacin de la lista:


Insertar un nuevo elemento supondr apilar en la pila de la izquierda.
Eliminar, se limita a desapilar de la pila de la derecha.
Consultar el punto de inters slo es mirar la cima de la pila de la derecha.
Y diremos que la lista est vaca si lo estn ambas pilas.

Respecto del punto de inters:


IrAInicio es poner toda la informacin que hay en la pila de la izquierda, sobre la pila
de la derecha (desapilar de izquierda y apilar en derecha).
Avanzar el punto de inters supondr desapilar de la pila de la derecha y apilar a la
izquierda.
Y diremos que el punto de inters est al final de la lista si la lista de la derecha est
vaca.

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.

13.3.4 Variantes de las implementaciones de listas enlazadas


En la implementacin dinmica de las listas hemos visto que existan diferentes casos
especiales que complicaban ligeramente la implementacin de los mtodos. Para evitar estos
casos especiales existen diferentes mejoras que pueden aplicarse por separado o combinadas
en la implementaci segn nos interese mejorar ciertos aspectos de la implementacin.

Listas doblemente enlazadas


Hemos visto que en algunos de los algoritmos exista el problema de poder acceder al
elemento anterior al punto de inters. Este hecho nos llevaba a tener que hacer operaciones
que evitasen tener que acceder a dicho elemento y en caso de no poder evitarlo, implementar
un recorrido desde el inicio de la lista hasta poder localizar el elemento (lo que tiene un coste
lineal respecto al tamao de la lista). Esta situacin se podra resolver fcilmente si, dado un
nodo de la lista, se pudiese conocer el elemento anterior. De igual manera que se puede
conocer el elemento siguiente.
La manera de hacerlo es mediante la utilizacin de un puntero adicional en cada nodo que
proporcione esa informacin. Esto da lugar a las llamadas listas doblemente enlazadas.
Con esta modificacin ciertas tareas mejoraran (aumentara la eficiencia temporal) pero el
tamao de cada elemento aumenta (disminuye la eficiencia espacial de la estructura).

Pg. 18
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)

pto
ini fin
24

La especificacin de datos en la parte privada de la clase Lista quedara como sigue:

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.

ini pto fin


24

Listas con nodo cabeza


Finalmente, otro de los problemas que hemos tenido en la implementacin de las listas
enlazadas (dinmicas) ha sido el tener que comprobar en cada momento si la lista tena o no
elementos.
Si nos aseguramos que la lista siempre posee un elemento, esta comprobacin sera
innecesaria.
La solucin consiste en introducir al inicio de la lista un elemento ficticio que no contiene
realmente ninguna informacin vlida, pero que evita que la lista vaca sea slo un puntero
apuntando a NULL. De esta forma, dentro de la lista no existen punteros nulos y siempre
existe, al menos, un nodo. Aunque la lista est vaca.

Pg. 19
Tema 13. Listas

La lista vaca tendra, grficamente, la siguiente estructura:


cabeza

pto

y cuando la lista tiene ms de un elemento tendra la siguiente forma:

pto
cabeza
? 32 7 24 86 25

A continuacin se muestra la implementacin en C++ de una estructura que combinara todas


las variantes comentadas: una lista doblemente enlazada, circular y con nodo cabeza.
La interfaz de la clase sera:
class Lista
{
public:
Lista (void);
Lista (const Lista &);
~Lista (void);
const Lista & operator= (const Lista &);
void Insertar (Valor);
bool Eliminar (void);
bool Consultar (Valor &);
bool ListaVacia (void);
void IrAInicio (void);
bool Avanzar (void);
bool FinalLista (void);

private:
struct Nodo;
typedef Nodo* Puntero;
struct Nodo
{
Valor info;
Puntero sig;
Puntero ant;
};

Puntero cab; /* Puntero al inicio de la lista */

Pg. 20
Alg. y Estr. Datos-I / Fund. Progr.-II (R. Ferrs, J. Albert)

Puntero pto; /* Punto de inters */


};

Y la implementacin de los diferentes mtodos:

/*
* 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;

/* Se vaca la lista sobre la que se copia */


Vaciar();

/* Se recorre la lista original */


p_aux = ori.cab->sig;
while (p_aux != ori.cab)
{
/* Se reserva hueco y actualiza la informacion */
q_aux = new Nodo;
q_aux->info = p_aux->info;

/*
* 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

You might also like