Fundamentos de Programación Cap 5 y Cap 6 Heileman, Gregory

Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 8

CAPITULO 5

LISTAS
Las listas son una estructura de datos más fundamentales de entre las empleadas para almacenar
una colección de elementos. La importancia del TAD Lista reside en que puede usarse para
implementar una amplia variedad de otros TADs.

5.1. EL TAD LISTA

Una lista puede definirse como una n-tupla dinámica ordenada, el término de “dinámica”
en esta definición se justifica con el fin de resaltar que los elementos en esta n-tupla pueden
cambiar con el tiempo.
El primer elemento de la lista, se denomina “CABEZA” de la lista, mientras que el último
elemento, se conoce como la “COLA” de la lista. El número de elementos de una lista se refiere a
la longitud de la lista.
Si todos los elementos almacenados de una lista son del mismo tipo, entonces se dice que
la lista es “HOMOGENEA”. Si distintos tipos de elementos están almacenados en la lista, entonces
la lista se dice que es “HETEROGENEA”.
A continuación, se ofrecen las operaciones que definiremos para acceder a los elementos
de las listas:

1. Insertar(L,x,i). Añade el elemento x a L en la posición i, haciendo que los


elementos Li, Li+1, …, Ln, pasen a ser los elementos Li+1, Li+2, …, Ln+1 y que la longitud
de la lista se n+1. Si la operación tiene éxito, se devuelve “VERDADERO”, en otro
caso se devuelve “FALSO”.
2. Añadir(L,x). Añade un elemento x a la “COLA” de L, haciendo que la longitud de la
lista se n+1. Si la operación tiene éxito, se devuelve “VERDADERO”, en otro caso
se devuelve “FALSO”.
3. Obtener(L,i). Devuelve el elemento almacenado en la posición i de L, o el valor
nulo si la posición i no existe.
4. Eliminar(L,i). Elimina el elemento almacenado en la posición i de L, haciendo que
los elementos Li+1, Li+2, …, Ln pase a ser los elementos Li, Li+1, …, Ln-1, y que la
longitud de la lista sea n-1. Si la operación tiene éxito, se devuelve “VERDADERO”,
en otro caso se devuelve “FALSO”.
5. Longitud(L). Devuelve la longitud de L.
6. Inicio(L). Sitúa la posición actual de L a la “CABEZA”.
7. Actual(L). Devuelve la posición actual de L.
8. Siguiente(L). Incrementa la posición actual de L y devuelve su valor. Si la posición
actual es i, la posición actual pasa a ser i+1 y se devuelve i+1.

Las operaciones Insertar, Añadir, Eliminar, Inicio y Siguiente modifican las listas a las
que son aplicadas. El resto de las operaciones simplemente consultan las listas con el
fin de obtener información acerca de ellas.
Se asume que una lista tiene una “VARIABLE DE POSICIÓN” actual que se refiere a cierto
elemento de la lista. Esta variable puede usarse para iterar sobre los elementos de una lista.

5.2. DISPOSICIÓN SECUENCIAL.

Si todos los elementos que forman una estructura de datos dada están almacenados uno
detrás de otro en posiciones de memoria consecutivas, decimos que la estructura de datos tiene
“DISPOSICIÓN SECUENCIAL” en la memoria de la computadora. La “DISPOSICIÓN SECUENCIAL”
hace posible acceder a cualquier elemento de la estructura de datos en tiempo constante. Dada la
dirección de comienzo de la estructura de datos en memoria, podemos encontrar la dirección de
cualquier elemento de la estructura de datos simplemente calculando su desplazamiento a partir
de la dirección de comienzo. Un “ARRAY” es un ejemplo de estructura de datos con “DISPOSICIÓN
SECUENCIAL”, debido a que se tarda la misma cantidad de tiempo en acceder al cualquier
elemento, una estructura de datos con “DISPOSICIÓN SECUENCIAL” también se denomina una
“ESTRUCTURA DE DATOS DE ACCESO DIRECTO”.
El TAD Lista puede ser implementado usando “DISPOSICIÓN SECUENCIAL” si se asigna en
tiempo de compilación un array de posiciones de memoria que sea suficientemente grande para
mantener todos los elementos de la lista. Las ventajas ofrecidas por este enfoque incluyen su
simplicidad, así como la eficiencia con la que las operaciones del TAD Lista pueden ser
implementadas. La principal desventaja que resulta de asignar el array en tiempo de compilación
es que debe establecerse un límite a priori sobre el número de elementos que pueden ser
almacenados en las listas. Esto puede conducir a problemas en tiempo de ejecución.

5.2.1. OPERACIONES DE LAS LISTAS.

Operación Obtener: Puede ser implementada en tiempo.


Operación Insertar: necesita tiempo, dado que la integridad de la “DISPOSICIÓN
SECUENCIAL” debe de ser mantenida después de una inserción.
Operación Longitud: Puede ser implementada en tiempo constante, almacenando la
longitud de la lista en una variable.
Operación Añadir: Puede llevarse a cabo en tiempo ya que no necesita ningún
desplazamiento.
Operación Inicio: Puede ser implementada en tiempo si se utiliza una variable para
almacenar la posición actual de una lista.
Operación Siguiente: Puede ser implementada en tiempo si se utiliza una variable para
almacenar la posición actual de una lista.

Antes de comentar una estrategia común para asignar más memoria, definimos primero el
“FACTOR DE CARGA” de un array como el número de elementos almacenados en el array, dividido
por el tamaño del array. Basándonos en esta definición, la estrategia es como sigue: Cuando el
factor de carga alcance 1, asignar dinámicamente un nuevo array cuyo tamaño sea el doble del
tamaño del array actual, entonces copiar todos los elementos almacenados en el array actual en el
nuevo array.

5.2.2. IMPLEMENTACION CON ARRAYS.

Clases iteradoras: Resulta útil abstraer los aspectos de control de un tipo de datos. Esto
habitualmente se conseguirán suministrando un iterador con una implementación de un tipo de
datos. Un “ITERADOR” encarna la abstracción del control en la secuenciación de los elementos de
un tipo de datos. Esto nos permite mantener una visión abstracta del flujo de control en nuestras
implementaciones de tipos de datos.

5.2.3. OPERACIONES DE LOS CONJUNTOS DINÁMICOS.

Para la operación Obtener se tendrá que buscar en toda la lista antes de que se encuentre
el elemento deseado. También tendrá que buscarse en la lista entera con el fin de determinar que
un elemento no está en la lista. Si la lista contiene n elementos, la operación Buscar requiere
tiempo utilizando la implementación dada, este planteamiento de fuerza bruta se conoce como
“BÚSQUEDA LINEAL O SECUENCIAL”.
Búsqueda Binaria: Una búsqueda binaria comienza comparando el elemento central de la
lista ordenada con la clave de búsqueda. Esto divide efectivamente la lista en dos partes, una
mitad inferior y una mitad superior. Como la lista está ordenada, la clave de cada elemento de la
mitad inferior de la lista debe ser menor que la clave de cada elemento de la mitad superior de la
lista. Si la clave de búsqueda es igual a la clave del elemento central, entonces hemos encontrado
el elemento deseado y la búsqueda termina. Sin embargo, si la clave de búsqueda es menor que la
clave del elemento central, entonces se examina el elemento central de la mitad inferior de la
lista. En otro caso, la clave de búsqueda debe ser mayor que la clave del elemento central, y se
examina el elemento central de la mitad superior de la lista. La lista continúa dividiéndose de esta
forma hasta que, o bien se encuentra el elemento deseado, o bien se ha buscado en toda la lista.
La operación Insertar del TAD conjunto dinámico puede ser implementada en tiempo si
no nos preocupamos de mantener la lista ordenada y el array es suficientemente grande como
para almacenar el nuevo elemento. Simplemente utilizamos la operación Añadir de la TAD Lista
para incorporar cada elemento a la cola de la lista, si se utiliza la técnica de la búsqueda binaria
para la operación Buscar, entonces el orden de la lista debe ser preservado después de la
inserción de cada nuevo elemento. La operación Eliminar también requerirá tiempo, dado que le
elemento que queremos eliminar debe ser encontrado, y en el caso peor se encuentra en la
cabeza de la lista.

Listas Auto-organizadas: Otro planteamiento utilizado para reducir el tiempo requerido


por la operación Buscar de conjunto dinámico, supone reordenar los elementos de la lista
conforme a reglas heurísticas que pretenden colocar los elementos que son más frecuentemente
solicitados hacia el inicio de la lista. Se emplea una búsqueda secuencial, comenzando por la
cabeza de la lista, para encontrar un elemento específico. La ordenación estadística óptima, es
cuando conocemos la probabilidad (raramente se conoce) de petición de cada elemento, y cada
petición es independiente de las otras peticiones de la secuencia, el mejor planteamiento es
almacenar los elementos de la lista en orden no decreciente de probabilidad de petición, y nunca
reordenarlos.
Las técnicas que reordenan los elementos de la lista basándose únicamente en la
secuencia de peticiones que se han producido hasta el momento se conocen como heurísticas de
auto-organización. Tres heurísticas comúnmente utilizadas son:
1. Mover al frente: Después de la búsqueda con éxito de una clave, mover el
elemento correspondiente al frente (cabeza) de la lista, sin cambiar el orden
relativo de los otros elementos. Si se inserta un elemento, colocarlo al frente
de la lista.
2. Transponer: Después de la búsqueda con éxito de una clave, intercambiar el
elemento correspondiente con el elemento que está inmediatamente delante
de la lista. Si se inserta un elemento, colocarlo al frente de la lista.
3. Recuento de frecuencia: El recuento de cada elemento (que se inicializa a
cero) se incrementa cuando se inserta un elemento, o cuando el elemento es
el resultado de una petición de búsqueda. Este recuento se pone a cero
cuando el elemento es eliminado. La lista se mantiene de tal forma que los
elementos aparecen en orden no decreciente del recuento de frecuencia. Esta
técnica requiere un campo recuento adicional para cada elemento de la lista,
por lo que requiere memoria adicional.

En la práctica estas estrategias conducen a menudo a tiempos de búsquedas mejorados.

5.3. LISTAS ENLAZADAS.

Es otro planteamiento para implementar el TAD Lista, asigna memoria para almacenar los
elementos de la lista conforme se necesita durante la ejecución, y conecta los elementos de la lista
usando punteros. La memoria es desasignada cuando ya no se necesita más un elemento de la
lista. Una lista enlazada se representa por la secuencia de nodos conectados por enlaces.
Lista simplemente enlazada: Debido a que cada nodo de la lista está conectado al
siguiente por un solo enlace. El nodo contiene dos campos datos (contiene un elemento de la
lista) y siguiente (almacena un enlace al siguiente nodo de la lista).

Lista doblemente enlazada: Cada nodo en una lista doblemente enlazada contiene tres
campos un campo almacena un elemento de la lista y los otros dos almacenan enlaces a los
nodos precedente y siguiente de la lista, en este caso se usan punteros nulos para marcar ambos
extremos de la lista.

Lista enlazada circular: En lugar de colocar el puntero nulo en el campo siguiente del nodo
cola, almacenamos un puntero a la cabeza de la lista.
Lista doblemente enlazada circular: Necesitaríamos almacenar un puntero al nodo cola en
el campo anterior del nodo cabeza.

A menudo se añaden nodos mudos denominados “Centinelas” para enlazar listas, estos
pueden utilizarse para almacenar información acerca de una lista, o para simplificar la
comprobación de condiciones de límite en una lista.

5.3.1. OPERACIONES DE LAS LISTAS.

En las listas enlazadas ya no tenemos acceso directo a un elemento arbitrario de la lista,


debemos recorrer la lista, comenzando por la cabeza, hasta llegar al elemento deseado. En las
listas doblemente enlazadas podemos desplazarnos hacia adelante y hacia atrás, mientras que en
las listas simplemente enlazadas no es posible desplazarse hacia atrás a otros nodos de la lista.
Esto hace que sea difícil realizar las reasignaciones de punteros necesarias durante las operaciones
Insertar o Eliminar.

5.3.2. IMPLEMENTACIÓN CON LISTAS ENLAZADAS.

5.3.3. OPERACIONES DE LOS CONJUNTOS DINÁMICOS.

La heurística de auto-organización puede ser aprovechada por una implementación del


TAD conjunto dinámico que utilice listas enlazadas, la heurística de mover al frente puede
implementarse más eficientemente de lo que se podía con una estructura de datos con
disposición secuencial, dado que el elemento que se mueve simplemente se empalma en la cabeza
de la lista enlazada.
Listas con saltos: la principal debilidad de las listas enlazadas, es la incapacidad para
acceder de manera directa a elementos arbitrarios de la lista durante una búsqueda. Las listas con
saltos mejoran el tiempo de búsqueda añadiendo punteros adicionales a ciertos nodos de la lista.
Estos punteros “saltan” un número fijo de nodos en las listas. Si la lista se mantiene ordenada,
estos punteros de salto pueden ser usados para realizar un tipo de búsqueda binaria.

5.4. GESTIÓN DE LA MEMORIA.

La gestión de la memoria supone la administración del recurso limitado de la memoria por


medio del empleo de distintas estrategias. La zona de memoria libre es vistas como una “caja
negra”. Con esto queremos decir que la memoria libre responde a la operación “new” reservando
alguna porción de su memoria, y que responde a la operación “delete” liberando alguna porción
de su memoria.
Siempre debería de estudiarse un programa para determinar el coste adicional debido a la
gestión de memoria “antes” de intentar optimizarlo con rutinas suministradas por el usuario.

5.5. UN EJEMPLO ELABORADO: MATRICES DISPERSAS.

Una matriz dispersa contiene relativamente pocos elementos no nulos. Así la mayoría del
almacenamiento en este caso está ocupada por elementos 0.
El operador (-) se sobre carga dos veces, una para el vector negativo y otra para la resta
entre vectores.
El operador (+) se sobre carga para realizar la suma de vectores.
El operados (*) se sobre carga tres veces, la primera se utiliza cuando queremos
multiplicar un vector por un escalar, la segunda se utiliza para calcular el producto escalar de dos
vectores y la tercera se utiliza para calcular el producto de un vector por una matriz.

Capítulo 6
PILAS Y COLAS
FIFO: Es una estructura en la que el primero que entra, es el primero que sale; lo que significa que
el primer elemento almacenado en este tipo será el primer elemento obtenido de ella. (Ejemplo
Cola).

LIFO: Es una estructura en la que el último que entra, es el primero que sale; lo que significa que el
último elemento que es almacenado en la estructura será el primer elemento obtenido de ella.
(Ejemplo Pila).

6.1. LOS TADs PILA Y COLA.

Pila: Conjunto dinámico que obedece la propiedad LIFO.


Cola: Conjunto dinámico que obedece la propiedad FIFO.
El orden temporal de inserciones en estos conjuntos determina completamente el orden
en el cual los elementos son obtenidos a partir de ellos.

6.1.1. OPERACIONES DE LAS PILAS.


En cada una de las operaciones del TAD pila que se dan a continuación, “x” representa a
un elemento y “S” a una pila arbitraria.
Operación Apilar(S,x): Insertar “x” en “S”.
Operación Cima(S): Devuelve el elemento que fue insertado más recientemente en “S”.
Operación Desapilar(S): Elimina el elemento que fue insertado más recientemente en “S”.
En la practica la operación “Desapilar” a menudo se implementa de tal forma que
devuelve el elemento insertado más recientemente antes de eliminarlo, realizar una operación
“Cima” o “Desapilar” en una pila vacía produce un error.
El único elemento al que tenemos acceso es el que ha sido insertado más recientemente,
al que nos referimos como “cima de la pila”, el elemento insertado más recientemente es 8, y el
elemento que lleva más tiempo en la pila es 2.
No es difícil implementar las operaciones “Apilar” y “Desapilar”, utilizando tanto una lista
como disposición secuencial, como una lista enlazada. Para ambas formas de lista tenemos dos
opciones básicas: podemos mantener la “cima” de la pila en la “cabeza” o en la “cola” de la lista.
Con una lista con disposición secuencial es más eficiente mantener la “cima” de la pila en la “cola”
de la lista. En este caso, en una operación “Apilar” simplemente añadimos un nuevo elemento a la
“cola” de la lista, y en la operación “Desapilar” eliminamos un elemento de la “cola” de la lista.
Debido a que una lista con disposición secuencial permite accedo directo, ambas
operaciones pueden implementarse.
Mantener la “cima” de la pila en la “cabeza” de la lista con disposición secuencial no
permite implementar tan eficientemente las operaciones del TAD pila. Para ver por qué,
recuérdese que insertar un nuevo elemento en la “cabeza” de una lista requiere que los
elementos de las posiciones 1 hasta n sean ascendidos a las posiciones 2 hasta n+1, la eliminación
de la “cabeza” de la lista también consume tiempo, dado que los elementos de las posiciones 2
hasta n deben de ser descendidos.
En una implementación con listas enlazadas, los elementos pueden ser almacenados tanto
en la cabeza como en la cola de la lista, y ambas operaciones “Apilar” y “Desapilar” pueden
implementarse.

6.1.2. OPERACIONES DE LAS COLAS.

En cada operación TAD cola, “x” de nuevo representa a un elemento y “Q” a una cola
arbitraria.
Operación Añadir(Q,x): Inserta x en Q.
Operación Primero(Q): Devuelve el elemento que lleva más tiempo en Q.
Operación Avanzar(Q): Elimina el elemento que lleva más tiempo en Q.

También podría gustarte