Está en la página 1de 34

3.

Listas

Estructuras de Datos
2o I.T. Informática, Gestión
Universidad de Burgos
Juan José Rodríguez Diez, jjrodriguez@ubu.es
Contenido

1. Definiciones

2. Tipo Abstracto de Datos

3. Implementaciones Contiguas

4. Implementación de Estructuras Genéricas

5. Implementaciones no Contiguas

6. Variaciones: Doblemente Enlazadas, Circulares

7. Aplicaciones

Estructuras de Datos 3. Listas 2/34


Referencias

• [Peña Marí, 2005], sección 6.3.1. Tipo Abstracto de Datos

• [Martí Oliet et al., 2005], capítulo 5. Ejercicios

• [Esakov and Weiss, 1989], capítulos 4 y 6

• [Deshpande and Kakde, 2004], capítulos 20 y 25

• [Ziviani, 2007], Sección 3.1 y apéndice C

• [Weiss, 2000], capítulo 16. Listas enlazadas

Estructuras de Datos 3. Listas 3/34


Introducción

• Definición 1 (Lista) secuencia (finita) de cero o más elementos de un tipo


determinado.

• A menudo se representa una lista como una sucesión de elementos sepa-


rados por comas a1 , a2 , . . . , an donde n ≥ 0 y cada ai es del tipo de los
elementos de la lista.

• El número n es la longitud de la lista. Si n = 0, se tiene una lista vacía.


Si n ≥ 1, se dice que a1 es el primer elemento de la lista y an el último
elemento.

• Se dice que el elemento ai está en la posición i, que ai precede a ai+1 para


i = 1, 2, . . . , n − 1 y que ai sucede a ai−1 para i = 2, 3, . . . , n.

• En principio consideraremos listas homogéneas. También es posible trabajar


con listas en las que los elementos pueden ser a su vez listas

Estructuras de Datos 3. Listas 4/34


TAD Lista
1 especificación LISTAS
2 parámetro
3 género elemento
4 fin parámetro
5 género lista
6 operaciones
7 [ ]: → lista // lista vacı́a
8 [  ]: elemento → lista // lista unitaria
9  ++ : lista lista → lista // concatenación
10    : elemento lista → lista // insertar izq
11    : lista elemento → lista // insertar der
12 longitud : lista → natural
13 parcial primero, último : lista → elemento
14 parcial sin primero : lista → lista
15 parcial sin último : lista → lista
16 fin especificación

Estructuras de Datos 3. Listas 5/34


TAD Elemento

1 especificación ELEMENTO
2 género elemento
3 operaciones
4  =  : elemento elemento → booleano
5 fin especificación

• Propiedades: reflexiva, simétrica, transitiva

Estructuras de Datos 3. Listas 6/34


Completando las Listas

1 especificación MÁS SOBRE LISTAS


2 usa LISTAS
3 operaciones
4 parcial  @ : lista natural → elemento
5 parcial modificar : lista natural elemento → lista
6 parcial insertar : lista natural elemento → lista
7 parcial borrar : lista natural → lista
8 buscar : elemento lista → natural
9 fin especificación

Estructuras de Datos 3. Listas 7/34


Implementaciones Contiguas

• Los elementos se almacenan consecutivamente, de acuer-


do a su posición

• Un vector y el número de elementos

1 #define MAX LISTA 100


2

3 typedef struct {
4 int numero;
5 TipoElemento elementos[ MAX LISTA ];
6 } Lista ;

Estructuras de Datos 3. Listas 8/34


Implementaciones Contiguas (II)

1 void inicializar ( Lista ∗pl ) {


2 pl−>numero = 0;
3 }
4

5 int longitud ( Lista ∗pl ) {


6 return pl−>numero;
7 }
8

9 int vacia ( Lista ∗pl ) {


10 return pl−>numero == 0;
11 }
12

13 int llena ( Lista ∗pl ) {


14 return pl−>numero == MAX LISTA;
15 }

Estructuras de Datos 3. Listas 9/34


Implementaciones Contiguas (III)

1 TipoElemento primero( Lista ∗pl ) {


2 assert ( ! vacia ( pl ) );
3 return pl−>elementos[ 0 ];
4 }
5

6 TipoElemento ultimo( Lista ∗pl ) {


7 assert ( ! vacia ( pl ) );
8 return pl−>elementos[ pl−>numero − 1 ];
9 }
10

11 TipoElemento elemento( Lista ∗pl , int posicion ) {


12 assert ( 1 <= posicion );
13 assert ( posicion <= pl−>numero );
14 return pl−>elementos[ posicion − 1 ];
15 }

Estructuras de Datos 3. Listas 10/34


Implementaciones Contiguas (IV)

1 void insertar ( Lista ∗pl , int posicion , TipoElemento valor ) {


2 int i ;
3 assert ( 1 <= posicion );
4 assert ( posicion <= pl−>numero + 1 );
5 assert ( ! llena ( pl ) );
6 for( i = pl−>numero; i >= posicion; i−− )
7 pl−>elementos[ i ] = pl−>elementos[ i − 1 ];
8 pl−>elementos[ posicion − 1 ] = valor ;
9 pl−>numero++;
10 assert ( elemento( pl , posicion ) == valor );
11 }

Estructuras de Datos 3. Listas 11/34


Implementaciones Contiguas (V)

1 void insertar primero ( Lista ∗pl , TipoElemento valor ) {


2 insertar ( pl , 1, valor );
3 }
4

5 void insertar ultimo ( Lista ∗pl , TipoElemento valor ) {


6 insertar ( pl , longitud ( pl ) + 1, valor );
7 }
8

9 void modificar ( Lista ∗pl , int posicion , TipoElemento valor ) {


10 assert ( 1 <= posicion );
11 assert ( posicion <= pl−>numero );
12 pl−>elementos[ posicion − 1 ] = valor ;
13 assert ( elemento( pl , posicion ) == valor );
14 }

Estructuras de Datos 3. Listas 12/34


Implementaciones Contiguas (VI)
1 void eliminar ( Lista ∗pl , int posicion ) {
2 int i ;
3 assert ( 1 <= posicion );
4 assert ( posicion <= pl−>numero );
5 for( i = posicion ; i < pl−>numero; i++ )
6 pl−>elementos[ i − 1 ] = pl−>elementos[ i ];
7 pl−>numero−−;
8 }
9

10 void eliminar primero ( Lista ∗pl ) {


11 eliminar ( pl , 1 );
12 }
13

14 void eliminar ultimo ( Lista ∗pl ) {


15 eliminar ( pl , longitud ( pl ) );
16 }

Estructuras de Datos 3. Listas 13/34


Implementaciones Contiguas (VII)

1 int buscar( Lista ∗pl , TipoElemento valor ) {


2 int i ;
3 for( i = 0; i < pl −> numero; i++ )
4 if ( pl−>elementos[ i ] == valor )
5 return i + 1;
6 return 0;
7 }
8

9 void concatenar( Lista ∗pl1 , Lista ∗pl2 ) {


10 int i ;
11 assert ( pl1 != pl2 );
12 for( i = 1; i <= longitud( pl2 ); i ++ )
13 insertar ultimo ( pl1 , elemento( pl2 , i ) );

Estructuras de Datos 3. Listas 14/34


Listas de Distintos Tamaños

• Solución: en vez de utilizar un vector, utilizar un puntero

• El procedimiento inicializar recibiría un argumento adicional con el ta-


maño deseado. Procedimiento adicional para finalizar
1 typedef struct {
2 int numero, maximo;
3 TipoElemento ∗elementos;
4 } Lista ;
5

6 void inicializar ( Lista ∗pl , int maximo ) {


7 assert ( maximo > 0 );
8 pl−>numero = 0; pl−>maximo = maximo;
9 pl−>elementos = calloc( maximo, sizeof( TipoElemento ) );
10 assert ( pL−>elementos != NULL );
11 }

Estructuras de Datos 3. Listas 15/34


Cambiar de Tamaño Dinámicamente

• Utilizando punteros en vez de vectores, podemos cambiar el tamaño


utilizando la función
void ∗ realloc ( void ∗ptr , size t size )

• Buscar espacio cada vez que se cambia de tamaño sería demasiado


costoso

• Cuando se llene el espacio disponible, pedir espacio para unos cuantos

• Liberar memoria sólo cuando desperdiciemos una cantidad significati-


va.

Estructuras de Datos 3. Listas 16/34


Listas de Distintos Tipos
• Primera solución: el preprocesador

1 #define DECLARA LISTA( TipoElemento ) \


2 typedef struct { \
3 int numero; \
4 TipoElemento elementos[ MAX LISTA ]; \
5 } Lista ##TipoElemento; \
6 void Lista ##TipoElemento## inicializar \
7 ( Lista ##TipoElemento ∗pl ) { \
8 pL−>numero = 0; \
9 }
1 DECLARA LISTA(int); DECLARA LISTA(double);
2 main( ){
3 Lista int l1 ; Lista double l2 ;
4 Lista int inicializar ( &l1 );
5 Lista double inicializar ( &l2 );

Estructuras de Datos 3. Listas 17/34


Listas de Distintos Tipos (II)

• Solución 2: manejo directo de la memoria


1 typedef struct {
2 int numero, maximo;
3 size t tamElemento;
4 void ∗elementos;
5 } Lista ;
6 #define POS(L,P) \
7 ( (L).elementos + (P) ∗ (L).tamElemento )
1 void inicializar ( Lista ∗pl , int maximo, size t tamElemento) {
2 pl−>numero = 0; pl−>maximo = maximo;
3 pl−>tamElemento = tamElemento;
4 pl−>elementos = calloc( maximo, tamElemento );
5 }

Estructuras de Datos 3. Listas 18/34


Listas de Distintos Tipos (III)

1 void elemento( Lista ∗pl , int posicion , void ∗px ) {


2 memcpy( px, POS( ∗pL, posicion − 1 ),
3 pl−>tamElemento ) ;
4 }

• Para insertar y eliminar, utilizar memmove


1 main(){
2 Lista l1 ;
3 int i ;
4

5 inicializar ( &l1, 10, sizeof( int ) );


6 insertar ( &l1, 1, &i );
7 elemento( &l1, 1, &i );
8 }

Estructuras de Datos 3. Listas 19/34


Realizaciones no Contiguas
• Concepto de posición: entero índice, puntero. . .

• Operaciones adicionales: anterior , siguiente , final . . .

• Cada elemento de la lista se almacena en un nodo

• Los nodos se identifican por su posición

• Los nodos contienen un elemento y la posición del siguiente nodo

• El nodo que contiene al último elemento tendrá un valor especial en el


campo de la posición del siguiente nodo

• Se elimina la necesidad de tener que mover los elementos que están


detrás al insertar o eliminar

• Inconveniente: la memoria adicional requerida para almacenar la posi-


ción del siguiente nodo

Estructuras de Datos 3. Listas 20/34


Basadas en Punteros (o Referencias)
• La posición vendrá dada por un puntero

• Se puede utilizar una celda como cabecera que no contendrá ningún


elemento y apuntará a a1 , ya que simplifica los algoritmos.

a1 a2 ... an
cabecera
• Cada nodo será un registro. El tipo de la posición y el tipo de la lista es
el de un puntero a un nodo

• La posición i será un puntero a la celda que contiene el puntero a la


celda que contiene el elemento ai

• La primera posición será un puntero a la cabecera. El fin de la lista será


un puntero a la última celda
Estructuras de Datos 3. Listas 21/34
Basadas en Punteros (o Referencias) (II)
1 typedef struct nodo {
2 TipoElemento elemento;
3 struct nodo ∗ siguiente ;
4 } Nodo;
5 typedef Nodo ∗TipoPosicion;
6 typedef Nodo ∗Lista;
1 void inicializar ( Lista ∗pl ) {
2 assert ( pl != NULL );
3 ∗pl = ( Lista ) malloc( sizeof ( Nodo ) );
4 assert ( ∗pl != NULL );
5 ( ∗ pl )−>siguiente = NULL;
6 }
1 TipoPosicion inicio ( Lista ∗pl ) {
2 return ∗pl ;
3 }

Estructuras de Datos 3. Listas 22/34


Basadas en Punteros (o Referencias) (III)

1 TipoPosicion fin ( Lista ∗ pl ) {


2 TipoPosicion p;
3 for( p = ∗pl ; p−>siguiente != NULL; p = p−>siguiente );
4 return p;
5 }

• ¿Por qué no basta con mantener un puntero al último?

1 TipoPosicion siguiente ( Lista ∗pl , TipoPosicion p ) {


2 return p−>siguiente;
3 }
1 TipoElemento recuperar( Lista ∗pl , TipoPosicion p ) {
2 return p−>siguiente−>elemento;
3 }

Estructuras de Datos 3. Listas 23/34


Basadas en Punteros (o Referencias) (IV)

1 TipoPosicion localizar ( Lista ∗pl , TipoElemento x ) {


2 TipoPosicion p;
3 assert ( ∗pl != NULL );
4 for( p = ∗pl ; p−>siguiente != NULL; p = p−>siguiente )
5 if ( p−>siguiente−>elemento == x ) break;
6 return p;
7 }
1 void insertar ( Lista ∗pl , TipoPosicion p, TipoElemento x ){
2 TipoPosicion tmp;
3 assert ( p != NULL ); tmp = p−>siguiente ;
4 p−>siguiente = ( TipoPosicion )malloc( sizeof ( Nodo ) );
5 assert ( p−>siguiente != NULL );
6 p−>siguiente−>elemento = x;
7 p−>siguiente−>siguiente = tmp;
8 }

Estructuras de Datos 3. Listas 24/34


Basadas en Punteros (o Referencias) (V)

1 void suprimir ( Lista ∗pl , TipoPosicion p ) {


2 TipoPosicion tmp;
3

4 tmp = p−>siguiente;
5 p−>siguiente = p−>siguiente−>siguiente;
6 free ( tmp );
7 }

Estructuras de Datos 3. Listas 25/34


Listas Basadas en Cursores
• Un puntero no es más que una dirección de memoria, un número

• Si un lenguaje no soporta el concepto de puntero (o algo similar) po-


demos “simularlo” utilizando enteros

• Almacenaremos los elementos de una lista en un vector. Pero los ele-


mentos no van a estar colocados en orden

• Cada elemento del vector tendrá dos campos, por un lado el elemento
de la lista y por otro lado un campo “entero” que indica la posición del
siguiente elemento dentro del vector

• Se pueden almacenar varias listas dentro del mismo vector. Identifica-


remos una lista por la posición de su primer elemento

• La posición de un elemento será un índice a la celda cuyo campo


siguiente apunte a dicho elemento

Estructuras de Datos 3. Listas 26/34


Listas Basadas en Cursores (II)

• Si el elemento es el primero de la lista su posición será un valor espe-


cial (-1)

• Existirá una lista especial con las posiciones disponibles.

• Aunque el lenguaje soporte punteros, esta representación puede ser


útil si el tipo de entero seleccionado ocupa menos que un puntero.

• Se pueden usar estrategias de “reasignación” de memoria.

• Inicialmente todos los elementos estarán en la lista de disponibles

• Para insertar se pasa la primera celda disponible a la lista en la posición


adecuada, y se le asigna el valor correspondiente. Para suprimir el
proceso es similar

Estructuras de Datos 3. Listas 27/34


Doblemente Enlazadas (o Indexadas)

• Si la lista es simplemente enlazada o indexada la operacion


anterior es muy costosa

• En cada nodo además de tener un puntero (o un índice) al


siguiente elemento, podemos tenerlo también al anterior

• Así podemos utilizar como posición un puntero (o índice) al


propio elemento

• Puede utilizarse con o sin cabecera

Estructuras de Datos 3. Listas 28/34


Listas Circulares

• En una lista simplemente enlazada el puntero al siguiente del último


tiene algún valor especial para indicar que no apunta a un elemento
válido

• Podemos utilizar ese puntero para apuntar al primer elemento, y ten-


dremos una lista circular

• En vez de tener un puntero al primero, podemos tener un puntero al


último ya que el siguiente del último es el primero

• Las listas circulares pueden tener o no tener cabecera y ser o no ser


doblemente enlazadas

Estructuras de Datos 3. Listas 29/34


Aplicaciones: Polinomios

• Un polinomio de una variable se ajusta a la siguiente fórmula: P(x) =


Pn i
i=0 ai x

• La primera opción a considerar sería la de un vector, pero si muchos


de los ai son 0 entonces no es aconsejable por cuestiones de espacio
y tiempo.

• Se puede tener una lista en la que cada elemento tenga dos campos:
el exponente y el coeficiente. Ordenada por el exponente

• Si el polinomio tiene varias variables:


? Variables fijas: cada elemento de la lista el coeficiente y el exponen-
te en cada una de las variables
? Distintas variables: cada elemento contendrá el coeficiente y una
lista con las variables y su exponentes

Estructuras de Datos 3. Listas 30/34


Aplicaciones: Enteros Grandes

• En ocasiones los enteros soportados por la máquina no


son suficientes.

• Dado un entero en base b, se ajusta a la siguiente fórmula:


X
n
ak b k
i=0

que se parece bastante a la fórmula del polinomio.

• Se puede usar una lista en la que cada elemento es un ak .

Estructuras de Datos 3. Listas 31/34


Matrices Dispersas

• Una matriz dispersa es aquella que tiene “muchos” ceros. Puede no


ser conveniente utilizar las matrices proporcionadas por el lenguaje

• Una fila de la matriz podría ser una lista, en la que cada elemento está
formado por la columna y el valor correspondiente

• La matriz podría ser un vector o una lista de las listas anteriores

• Si necesitamos recorrer por columnas, deberíamos tener una lista por


cada columna

• Cada elemento podría tener un registro con el valor, un puntero al si-


guiente de su fila y un puntero al siguiente elemento de su columna

• ¿Fila o columna de un elemento? Listas circulares con primer elemento


especial o almacenamiento en cada nodo

Estructuras de Datos 3. Listas 32/34


Referencias
[Deshpande and Kakde, 2004] Deshpande, P. and Kakde, O. (2004). C &
Data Structures. Charles River Media.
[Esakov and Weiss, 1989] Esakov, J. and Weiss, T. (1989). Data Structu-
res. An Advanced Approach Using C. Prentice-Hall.
[Martí Oliet et al., 2005] Martí Oliet, N., Ortega Mallén, Y., and Verdejo Ló-
pez, J. A. (2005). Estructuras de datos y métodos algorítmicos: ejercicios
resueltos. Pearson Prentice Hall.
[Peña Marí, 2005] Peña Marí, R. (2005). Diseño de Programas: Formalis-
mos y Abstracción. Prentice Hall, 3a edition.
[Weiss, 1998] Weiss, M. A. (1998). Data Structures and Problem Solving
Using Java. Addison Wesley.
[Weiss, 2000] Weiss, M. A. (2000). Estructuras de Datos en Java. Addison
Wesley. Traducción de [Weiss, 1998].
[Wikipedia, 2010a] Wikipedia (2010a). Linked list — wikipedia, the free
encyclopedia. http://en.wikipedia.org/wiki/Linked_list, [Online;
accessed 26-Octobre-2010].

[Wikipedia, 2010b] Wikipedia (2010b). Lista (informática) — wikipedia, la


enciclopedia libre. http://es.wikipedia.org/wiki/Lista_(inform%C3%
A1tica), [Internet; descargado 26-octubre-2010].

[Ziviani, 2007] Ziviani, N. (2007). Diseño de Algoritmos con Implementa-


ciones en Pascal y C. Thomson.

También podría gustarte