estructuras de datos lineales

37
Estructuras de Datos Lineales Materia: Estructuras de Datos Adriana Tovar Arriaga Mayo 2010

Upload: juliosvald

Post on 25-Jun-2015

194 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Estructuras de Datos Lineales

Estructuras de Datos Lineales

Materia: Estructuras de Datos

Adriana Tovar Arriaga

Mayo 2010

Page 2: Estructuras de Datos Lineales

Estructuras de Datos

• Muchos algoritmos requieren una representación apropiada de los datos para lograr ser eficientes.

• Esta representación junto con las operaciones permitidas se llama estructura de datos.

• Típicamente todas las estructuras de datos permiten inserciones arbitrarias.

• Las estructuras de datos varían en como permiten el acceso a los miembros del grupo.

• Algunas permiten tanto accesos como operaciones de borrado arbitrarios.

• Otras imponen restricciones, tales como permitir el acceso solo al elemento mas recientemente insertado, o al menos recientemente insertado.

Page 3: Estructuras de Datos Lineales

Estructuras como…

• Pilas

• Colas

• Listas enlazadas

• Árboles

• Árboles binarios de búsqueda

• Tablas Hash

• Colas de prioridad… etc

Page 4: Estructuras de Datos Lineales

¿Por qué necesitamos Estructuras de Datos?

• Una vez que la estructura de datos ha sido implementada, puede ser utilizada una y otra vez en diversas aplicaciones.

• Aquí veremos las correspondientes interfaces.• Este enfoque – separar la interfaz y la

implementacion – es parte del paradigma de programacion orientada a objetos.

• El usuario de la estructura de datos no necesita ver la implementación, solo las operaciones disponibles.

Page 5: Estructuras de Datos Lineales

¿Por qué necesitamos Estructuras de Datos?

• Sin embargo, otra parte importante de la programación orientada a objetos es la abstracción.

• Debemos pensar cuidadosamente el diseño de las estructuras de datos porque debemos escribir programas que utilicen esas estructuras de datos sin tener en cuenta su implementación.

• Esto hace la interfaz mas limpia, mas flexible (mas reutilizable) y generalmente más fácil de implementar.

Page 6: Estructuras de Datos Lineales

Las pilas

• La pila es una estructura de datos en la cual el acceso está limitado al elemento más recientemente insertado.

• El último elemento añadido a la pila es colocado en la cima, donde es accedido muy fácilmente, mientras que los elementos que llevan más tiempo son mas difíciles de acceder.

• Las tres operaciones naturales son insertar, eliminar y buscar, se renombran por apilar, desapilar y cima. (push, pop y top).

Page 7: Estructuras de Datos Lineales

Las Colas

• Otra estructura de datos simple son las colas. • En muchos casos, es importante poder encontrar y/o

eliminar el elemento mas recientemente insertado. • Pero en otras ocasiones, no sólo es importante, sino

que es la forma errónea de hacerlo. • Por ejemplo, esperamos que el trabajo menos

reciente o más viejo sea impreso antes.• Esto no es solamente justo, sino que es necesario

garantizar que el primer trabajo no espera para siempre.

Page 8: Estructuras de Datos Lineales

Las colas

• Operaciones básicas:– Insertar – insercion al final de la línea

– quitarPrimero – devuelve el primer elemento al frente de la línea y tambien lo elimina.

– Primero – acceso al elemento en el frente de la línea

• Históricamente quitarPrimero y primero han sido combinadas en una sola operación.

• Las operaciones de las colas consumen un tiempo constante por petición O(1).

Page 9: Estructuras de Datos Lineales

Colas - Implementación

• La forma mas sencilla de implementar una cola consiste en almacenar sus elementos en un vector, colocando el elemento en cabeza en la primera posición del mismo (es decir, en el índice 0).

• Si fin representa la posición del último elemento de la cola, entonces para insertar un elemento no tenemos mas que incrementar fin, insertando el elemento en esa posicion.

Page 10: Estructuras de Datos Lineales

Listas enlazadas

• En una lista enlazada, los elementos se almacenan de forma no contigua, en vez de en un vector de posiciones de memoria consecutivas.

• Para conseguir esto, cada elemento se almacena en un nodo que contiene el objeto y una referencia al siguiente nodo en la lista, como se muestra en la figura siguiente.

A0 A1 A2 A3

Una lista enlazada simple…

Page 11: Estructuras de Datos Lineales

Listas enlazadas…

• En este marco, se mantienen referencias al primer y ultimo nodo de la lista.

• Para ser mas precisos, un nodo típico es el siguiente:

class NodoLista

{

Object dato; //Algun elemento

NodoLista siguiente;

}

Page 12: Estructuras de Datos Lineales

Listas enlazadas…

• En cualquier momento, podemos añadir un nuevo último elemento x haciendo lo siguiente:

• Ahora, un elemento cualquiera no puede encontrarse con un único acceso; para ello debemos ir recorriendo la lista. Esto es similar a la diferencia entre acceder a un elemento en un disco compacto (un acceso) o en una cinta (secuencial).

ultimo.siguiente = new NodoLista(); //adjunta un nuevo NodoListaultimo = ultimo.siguiente; //ajusta el ultimoultimo.dato = x; //coloca x en el nodoultimo.siguiente = null; //es el ultimo;a justa

//siguiente

Page 13: Estructuras de Datos Lineales

Listas enlazadas…

• Aunque esto puede hacer pensar que las listas son menos atractivas que los vectores, tienen sus ventajas.– 1ro. Una inserción en medio de una lista no requiere mover todos los

elementos que se encuentran después del punto de inserción.– Los movimientos de los datos son muy costosos en la práctica, y las

listas enlazadas permiten la inserción con sólo una cantidad constante de instrucciones de asignación.

• Merece la pena hacer notar que si permitimos acceso sólo al primero, entonces tenemos una pila, y si permitimos inserciones solo en el ultimo y accesos solo por el primero, tenemos una cola.

Page 14: Estructuras de Datos Lineales

Listas enlazadas…

• Sin embargo, en estas situaciones necesitamos habitualmente operaciones mas generales, tales como encontrar o eliminar cualquier elemento de la lista, guiados por su nombre.

• También necesitamos ser capaces de insertar un elemento en cualquier posición. Esto es mucho mas de lo que permite una pila o una cola.

insertar buscar o eliminarcualquier elemento por nombre o por rango

Lista

Page 15: Estructuras de Datos Lineales

Listas enlazadas…

• Para acceder a los elementos en la lista, necesitamos una referencia al correspondiente nodo.

• Sin embargo, conceder este acceso viola claramente al principio de ocultamiento de la información.

• Necesitamos asegurar que cualquier acceso a la lista a través de una referencia es seguro.

• Para conseguir esto, definimos la lista en dos partes: una clase para las listas y una clase iteradora.

Page 16: Estructuras de Datos Lineales

Que veremos…

• Como permitir el acceso a cualquier elemento usando una lista enlazada general.

• Los algoritmos generales usados para las operaciones sobre listas enlazadas.

• Como la clase iteradora proporciona un mecanismo seguro para recorrer y acceder a las listas enlazadas.

• Variaciones de las listas, como las listas doblemente enlazadas y las listas enlazadas circulares.

Page 17: Estructuras de Datos Lineales

Ideas básicas

• La lista enlazada basica consta de una coleccion de nodos situados en la memoria dinamica conectados entre si.

• En una lista simplemente enlazada, cada nodo se compone de un dato y una referencia al siguiente nodo de la lista.

• El ultimo nodo de la lista contiene una referencia siguiente null.

• Se puede acceder al primer nodo de la lista a traves de un puntero, lo cual se muestra en la figura siguiente.

• Podemos imprimir la lista enlazada o buscar en ella comenzando en el primer elemento y siguiendo la cadena de punteros siguiente.

Page 18: Estructuras de Datos Lineales

Ideas básicas

• Las dos operaciones basicas que se pueden realizar son la insercion y eliminacion de un elemento arbitrario x.

• En caso de la insercion, debemos determinar donde va a tener lugar la insercion.

• Si partimos de una referencia a algun nodo de la lista, el lugar donde mas facilmente se puede insertar en el inmediatamente posterior a ese elemento.

a b c d

cabezaLista

Page 19: Estructuras de Datos Lineales

Ideas básicas

• En esta figura se muestra como insertamos x despues del elemento a en una lista enlazada.

• Debemos llevar a cabo los siguientes pasos:

tmp = new NodoLista(); //Crear un nodo nuevotmp.dato = x; // Colocar x en el campo dadoTmp.siguiente = actual.siguiente; //El siguiente al nodo x es bActual.siguiente = tmp; //El siguiente al nodo a es x

a

x

b... ...

actual tmp

Page 20: Estructuras de Datos Lineales

Ideas básicas

• Como resultado de estas instrucciones, la lista anterior ...a,b,... ahora es ...a,x,b... Podemos simplificar el codigo, puesto que NodoLista tiene un constructor que inicializa los atributos directamente. Asi obtenemos.

Tmp = new NodoLista (x, actual.siguiente); //Crear un nuevo nodo

Actual.siguiente = tmp; //El siguiente al nodo es a x

Page 21: Estructuras de Datos Lineales

Ideas básicas

• La eliminación se puede ejecutar con un simple cambio de una referencia. Saltando por encima del nodo.

• Necesitamos una referencia al nodo anterior al que queremos eliminar.

a b... ...

actual

x

actual.siguiente=actual.siguiente.siguiente;

Page 22: Estructuras de Datos Lineales

Ideas básicas

• La propiedad fundamental de las listas enlazadas es que se pueden hacer cambios en una lista realizando una cantidad constante de movimientos dados, lo cual es una gran mejora respecto a la implementación usando vectores, ya que la contigüidad en un vector implica que siempre que se añade o elimina un elemento, se deben desplazar todos los elementos que le siguen.

Page 23: Estructuras de Datos Lineales

Nodos cabecera:

• Un problema de la descripción básica es que asume que siempre que se elimina un elemento, hay algún elemento anterior a él.

• En consecuencia, la eliminación del primer elemento de la lista enlazada se convierte en un caso especial.

• De forma análoga, la rutina de inserción no nos permite insertar un elemento en la primera posición de la lista, ya que las inserciones están restringidas a aquellas posiciones posteriores a alguna otra.

• Una forma de lograr que se eviten estos casos especiales es introducir un nodo cabecera.

Page 24: Estructuras de Datos Lineales

Nodo cabecera:

• Un nodo cabecera es un nodo extra, que no guarda ningún dato, pero sirve para satisfacer el requerimiento de que cada nodo que contenga un elemento tenga un nodo anterior.

• Usando el nodo cabecera, simplificamos el código a cambio de una penalización despreciable en espacio.

• En aplicaciones más complejas, la cabecera no solamente simplifica el código, sino que también aumenta la velocidad, ya que, a la postre, un menor numero de test requiere un menor tiempo.

• Ahora veríamos la lista como sigue:

a b c

cabecera

Page 25: Estructuras de Datos Lineales

Nodo cabecera:

Pero debemos ser cuidadosos:• La rutina de impresión no debe imprimir el contenido

de la cabecera.• Las rutinas de búsqueda deben saltarse ese nodo. • Colocarnos al principio de la lista ahora significa

colocar la posición actual en cabecera.siguiente.• Una lista con nodo cabecera es vacía sicabecera.siguiente es null y se vería así:

cabecera

Page 26: Estructuras de Datos Lineales

Clases iteradoras

• La estrategia primitiva habitual identifica una lista enlazada con un puntero al nodo cabecera. Entonces se puede acceder a cada elemento individual de la lista proporcionando un puntero al nodo que lo contiene.

• El problema con esta estrategia es que es dificil comprobar si se producen errores. Un usuario podría cambiar un puntero de forma que apuntase a algo que está en otra lista.

• Una forma de garantizar que esto no puede pasar es almacenar la posicion actual como parte de la clase Lista.

• Para ello, añadimos un segundo atributo, actual.• Entonces, puesto que todos los accesos a la lista se realizan a

través de los métodos de la clase, podemos estar seguros de que actual siempre representa un puntero a un nodo de la lista, un puntero al nodo cabecera o bien es igual a null.

Page 27: Estructuras de Datos Lineales

Clases iteradoras

• Este esquema tiene un problema: puesto que solamente se dispone de una posición, no se soporta el caso en que dos iteradores necesiten acceder independientemente a la lista.

• Una forma de evitar este problema consiste en definir una clase iteradora separada.

• La clase Lista no mantendría entonces ninguna noción de posición actual y solamente contendría los métodos que tratan a la lista como una unidad, como esVacia y vaciar.

• Las rutinas que dependen del conocimiento de la posición de la lista en la que nos encontramos se encontrarían en la clase iteradora.

Page 28: Estructuras de Datos Lineales

Clases iteradoras

• Esta mantiene una noción de la posición actual y el nodo cabecera de la lista que representa. (el puntero al nodo cabecera puede ser inicializado por el constructor).

• El posibilitar el acceso a la lista se consigue haciendo que la clase iteradora pertenezca al mismo paquete.

• Podemos ver cada instancia de una clase iteradora como un marco en el que solamente se permiten operaciones legales sobre las listas como, por ejemplo, avanzar en la lista.

Page 29: Estructuras de Datos Lineales

Clases iteradoras

• Veamos un ejemplo de cómo funciona esto:

//Devuelve el numero de elementos que contiene laLista

static public int longLista( ListaEnlazada laLista )

{

int longitud = 0;

ListaEnlazadaIter itr = new ListaEnlazadaIter( laLista );

for ( itr.primero(); itr.estaDentro(); itr.avanzar())

longitud++;

return longitud;

}

Page 30: Estructuras de Datos Lineales

Listas doblemente enlazadas ylistas enlazadas circulares

• Las listas enlazadas no soportan de forma eficiente algunas operaciones importantes.

• Por ejemplo, aunque es fácil ir al principio de la lista, ir al final consume bastante tiempo.

• Aunque podemos avanzar fácilmente usando avanzar, la implementación de retroceder no se puede hacer de forma eficiente con una sola referencia siguiente.

• En algunas aplicaciones esto puede ser crítico.

Page 31: Estructuras de Datos Lineales

• La idea para implementar estas operaciones de forma eficiente, deberíamos tener dos referencias en cada nodo; una al siguiente nodo de la lista y otro al anterior.

• Para terminar de hacer simétrica la representación, añadiríamos además un nodo final además del nodo cabecera.

• Estas listas reciben el nombre de doblemente enlazadas.

Listas doblemente enlazadas ylistas enlazadas circulares

a b

cabecera final

Page 32: Estructuras de Datos Lineales

Listas doblemente enlazadas ylistas enlazadas circulares

• Ahora cada nodo contiene ahora dos referencias (siguiente y anterior), con lo que se pueden realizar de forma sencilla búsquedas y recorridos en ambos sentidos.

• Obviamente son necesarios algunos cambios pequeños pero importantes.

• En primer lugar, una lista vacía consiste ahora de un nodo cabecera y uno final conectados entre sí. Así:

cabecera final

Page 33: Estructuras de Datos Lineales

Listas doblemente enlazadas ylistas enlazadas circulares

• Observe que no son necesarios en los algoritmos cabecera.anterior y final.siguiente, por lo que ni siquiera se inicializan.

• La comprobación de lista vacía es ahora

cabecera.siguiente == final

O bien

final.anterior == cabecera

• Ya no volveremos a usar null para decidir si un avance nos ha llevado hasta el final de la lista, sino que lo sabemos cuando llegamos a cabecera o a final.

• El método retroceder se puede implementar mediante

actual = actual.anterior;

Page 34: Estructuras de Datos Lineales

Listas doblemente enlazadas ylistas enlazadas circulares

Inserción y eliminación• Naturalmente ahora podemos hacer insertarAntes e insertarDespues.

• En insertarDespues se hallan implicados el doble de cambios de punteros en las listas doblemente enlazadas que en las listas enlazadas simples.

• Si escribimos cada instrucción de forma explícita, obtenemos:

nNodo = new NodoListaEnlazadaDoble( );

nNodo.anterior = actual; //Asigna valor a x.siguiente

nNodo.siguiente = actual.siguiente; //Asigna valor a x.siguiente

nNodo.anterior.siguiente = nNodo; //Asigna valor a a.siguiente

nNodo.siguiente.anterior = nNodo; //Asigna valor a b.anterior

actual = nNodo;

Page 35: Estructuras de Datos Lineales

Listas doblemente enlazadas ylistas enlazadas circulares

• Al contrario de las listas enlazadas simples, ahora podemos eliminar el nodo actual porque tenemos acceso inmediato al nodo anterior. Por tanto, para eliminar x tenemos que cambiar la referencia siguiente de a y la anterior de b. Los cambios basicos serian:

a

x

b

1

3 2

4

... ...

Actual.anterior.siguiente = actual.siguiente; // Actualiza a.siguienteActual.siguiente.anterior = actual.anterior; //Actualiza b.anterior

actual

Page 36: Estructuras de Datos Lineales

Listas doblemente enlazadas ylistas enlazadas circulares

• Para realizar una implementacion de una lista doblemente enlazada, necesitamos decidir que operaciones se van a soportar.

• Es razonable esperar que haya el doble de operaciones que en las listas enlazadas simples.

• Cada procedimiento individual sera muy similar a las rutinas de las listas enlazadas simples, solo las operaciones dinamicas implican cambios adicionales en las referencias.

• Ejercicio: Implemente una clase de listas doblemente enlazadas, incluyendoa) Metodos irAPrincipio e irAFinal.b) Metodos primero y ultimo;c) Metodos insertarAntes e insertarDespues.d) Metodos buscarPrimero y buscarUltimo (la busqueda

comienza desde el principio y el final, respectivamente).e) Metodos eliminarPrimero y eliminarUltimo.

Page 37: Estructuras de Datos Lineales

Listas doblemente enlazadas ylistas enlazadas circulares

• Una practica habitual es crear una lista enlazada circular, en la que el ultimo nodo apunta al primero.

• Esto se puede hacer con o sin cabecera.

a b c d

a b c d

primero