Estructura de Datos Prof. Ivo Ovelar Lafarja Conceptos Generales: Estructuras Dinámicas: son aquellas que pueden cambiar su fisonomía o tamaño durante la ejecución del programa, aunque el acceso a los elementos que la componen es lento. Su necesidad viene dada por la rigidez de las Estáticas y buscando erradicar la desventaja y aprovechar al máximo el área de la memoria (segunda categoría de la eficiencia). Ventajas: Aprovechamiento al máximo del área de memoria. Nos conviene su uso cuando no se sabe la cantidad de elementos del conjunto de datos o varia mucho. Desventaja: Lentitud en el acceso a sus componentes; se necesita diseñar algoritmos para acceder o recorrer sus componentes. Listas: Lista: estructura de datos secuencial. Se clasifican en dependencia de la forma de acceder al siguiente elemento: lista densa: la propia estructura determina cuál es el siguiente elemento de la lista. Su tamaño no cambia en la ejecución del programa, es estático. Ejemplo: un array o vector. lista enlazada: la posición del siguiente elemento de la estructura la determina el elemento actual. Es necesario almacenar al menos la posición de memoria del primer elemento. Además es dinámica, es decir, su tamaño cambia durante la ejecución del programa. Listas Enlazadas: Una lista enlazada se puede definir recursivamente de la siguiente manera: una lista enlazada es una estructura vacía o un elemento de información y un enlace hacia una lista (a un nodo, el primero). Nodo: Unidad o componente que conforma una estructura dinámica, esta compuesto por un segmento de dato y uno o varios enlaces a otros nodos de igual o distinta confirguracion. Cuando se habla de una estructura dinámica no se habla de datos, sino de nodos, en donde en uno de sus segmentos se encuentra el dato a manipular o transformar, pero que por si solo no representa suficiente elemento para componer la estructura. Listas Enlazadas: Gráficamente se representa de la forma: Cabe señalar que estas estructuras a pesar de su lentitud, es muy fácil el mantenimiento de las misma, algo que es engorroso en las estáticas, en esta últimas conlleva el hecho de corrimiento o desplazamiento de información, algo que no es necesario en estas, con el simple hecho de crear un nuevo elemento (nodo), buscar el lugar y actualizar los enlaces es más que suficiente su actualización. Cabeza de la Lista: En las Estructuras Estáticas, en la TAM se reflejaba el nombre o acceso lógico a la misma, lo cual era posible porque el área que referencian es compacta; sin embargo en las Dinámicas no puede suceder lo mismo, pues sus componentes no necesariamente se van a ubicar en zonas contiguas, por tanto no vamos a tener un nombre o acceso lógico a estas estructuras. Entonces como acceder al primer componente?, para eso creamos el concepto de Cabeza de la Lista, un lugar donde vamos a almacenar la dirección del primer elemento (nodo) que la compone; por supuesto el primero a través de su enlace nos lleva al segundo, el segundo al tercero y así sucesivamente, hasta llegar al último; pero es el primero el que debemos tenerlo localizado directamente y esto lo garantizamos a través de la Cabeza. Operaciones con Listas: Inserción al comienzo de una lista: existen varios tipos de inserción; la inserción al comienzo o por la cabeza, consiste en insertar un nuevo nodo (el cual contendrá la nueva información) delante del actual primero; o sea debe convertir el actual primero en segundo o sucesivo de este nuevo y luego su dirección debe ser guardada en la cabeza, reconociendo así que se ha convertido en el primer nodo de la lista. Inserción por la cabeza: Sea la siguiente lista: x y z vacia cab Y queremos insertar la infomación, w, para eso creamos un nodo y lo insertamos al inicio de la lista w x cab y z vacia Seudo código INICIO Leer W Pedir memoria para representar el dato, area apuntada por aux. aux -> dato = W aux -> siguiente = vacio Si cab = vacio entonces cab = aux sino aux -> siguiente = cab cab = aux FinSi aux = vacio FIN Operaciones con Listas: Borrado del primer elemento: existen varias variantes de borrado, el borrado del primer elemento o borrado por la cabeza, consiste en convertir el actual segundo (de existir) en primer nodo de la lista y luego liberar el antiguo primer nodo de la lista. Borrado por la cabeza: Sea la siguiente lista, donde queremos eliminar la información x: y x cab z vacia Seudo código INICIO Si cab != vacio entonces aux = cab cab = cab -> siguiente Liberar memoria apuntada por aux aux = vacio FinSi FIN Operaciones con Listas: Recorrido de una lista: solo existe esta variante, la cual consiste en visitar o manipular los elementos de datos almacenados en todos los nodos de la estructura; esta operación nos permite trabajar con el conjunto de datos que hemos representado a través de ella. Para lograr esto, debemos acceder al primero (dirección guardada en la cabeza), manipular su segmento de dato, con el enlace de este pasar al segundo, manipular su segmento de datos y pasar a través del enlace al tercero, y así repetir la secuencia hasta llegar al último nodo de la lista. Recorrido de una lista: Sea la siguiente lista: x y cab p x y z z vacia Seudo código INICIO Si cab != vacio entonces aux = cab mientras aux != vacio visitar a aux -> dato aux = aux -> siguiente FinMientras FinSi FIN Listas Ordenadas Listas ordenadas: Las listas ordenadas son aquellas, en las que la posición de cada elemento depende de su contenido. Por ejemplo, podemos tener una lista enlazada que contenga el nombre y apellidos de un alumno y queremos que los elementos –información sobre los alumnos- estén en la lista en orden alfabético. 1 cab 2 4 32 vacia Creación de una lista ordenada: Cuando haya que insertar un nuevo elemento en la lista ordenada hay que hacerlo en el lugar que le corresponda, y esto depende del orden y de la clave escogidos. Este proceso se realiza en tres pasos: 1.- Reservar memoria para él (puede hacerse como segundo paso). Se usa un puntero auxiliar (nuevo) para reservar memoria. 2.- Localizar el lugar correspondiente al elemento a insertar. Se utilizan dos punteros: anterior y actual, que garanticen la correcta posición de cada enlace. 3.- Enlazarlo. Esta es la parte más complicada, porque hay que considerar la diferencia de insertar al principio, no importa si la lista está vacía, o insertar en otra posición. Se utilizan los tres punteros antes definidos para actualizar los enlaces. Inserción de nodo en una lista ordenada: Sea la siguiente lista: 1 2 32 vacia cab Y queremos insertar la infomación, 4, para eso creamos un nodo y lo insertamos en la posición correspondiente de la lista 4 1 cab 2 32 vacia Seudo código INICIO Leer W Pedir memoria para representar el dato, area apuntada por aux. aux -> dato = W aux -> siguiente = vacio Si cab = vacio entonces cab = aux sino anterior = actual = cab mientras (actual != vacio) & (actual->dato<aux->dato) anterior = actual actual = actual ->siguiente FinMientras Si anterior ! = actual entonces anterior ->siguiente = aux sino cab = aux FinSi aux ->siguiente = actual anterior = actual = vacio FinSI aux = vacio FIN Eliminación de nodos específicos Borrado de un elemento específico: es un proceso similar a la inserción, en el que debemos eliminar un nodo que posiblemente esté al inicio, al final o en el intermedio de nuestra lista, en cualquiera de los casos debemos primero localizarlo usando dos apuntadores (anterior, actual), de encontrarlo como consecuencia debemos para arreglar los enlaces de la lista sin perder su configuración y posteriormente liberar el área de la memoria que ocupa el nodo a eliminar. Borrado de un nodo en listas enlazadas: Sea la siguiente lista, donde queremos eliminar la información 2: 2 1 cab 3 vacia Otros tipos de listas enlazadas: Listas doblemente enlazadas y abiertas: son las listas en las que cada nodo tienen un enlace con el elemento siguiente y con el anterior. Una ventaja que tienen es que pueden recorrerse en ambos sentidos, ya sea para efectuar una operación con cada elemento o para insertar/actualizar y borrar. La otra ventaja es que las búsquedas son algo más rápidas puesto que ya hace referencia al elemento anterior. Su inconveniente es que ocupan más memoria por nodo que una lista simple. Tanto el siguiente al último, como el anterior al primero apuntan a la lista vacía. Ejemplo gráfico vacio cab x y vacio Otros tipos de listas enlazadas: Listas simples y circulares o cerradas: Las listas circulares o cerradas son aquellas en las que el último elemento (nodo), tiene un enlace con el primero, por lo que al vacío su último elemento como son los casos de las abiertas, de esta forma el apuntador no corre el riesgo de apuntar a una dirección que no sea la de un nodo que si pertenece a la lista. Ejemplo gráfico x cab y z Otros tipos de listas enlazadas: Listas doblemente enlazadas y circulares: estas listas son la combinación de los dos conceptos anteriores. Listas en donde cada elemento apunta al siguiente y al anterior, además de apuntar el último elemento al primero y el primero viceversa al último elemento de la lista. Para su implementación se usa un nodo llamado centinela, que es el que marca el inicio o fin de la lista, el cual tiene la misma característica de un nodo normal, con sus tres campos, dos de apuntadores y el de dato, lo único que en este último se pone una marca (dato acordado) para señalar la presencia del centinela, aumentando la cantidad de memoria necesaria para esta estructura. Ejemplo gráfico x y no dato centinela Otros tipos de listas enlazadas: Listas doblemente enlazadas, circulares y ordenadas: sabemos que la búsqueda o selección de información lleva un peso importante en nuestros algoritmos, pues gran parte del tiempo de ejecución o pasos de los mismos radica en este tipo de procesamientos; por tanto si buscamos la mayor rapidez en este tipo de estructuras, la mejor solución es el uso de listas doblemente enlazadas, circulares y ordenadas. Listas con enlaces reales, con doble direccionamiento y el ordenamiento de la información; el único inconveniente es la cantidad de memoria que se necesita, lo cual usted debe valorar a la hora de la implementación de su caso real. Ejemplo gráfico 1 2 no dato centinela Ejercicios: Haga en seudocódigo un algoritmo que nos permita contar la cantidad de elementos positivos y negativos que contiene una lista enlazada. Haga en seudocódigo un algoritmo que nos permita calcular el elemento mayor entre los elementos que contiene una lista enlazada.