Académique Documents
Professionnel Documents
Culture Documents
Hasta este punto se han visto las estructuras de datos presentadas por Arreglos,
Pilas y Colas, éstas son denominadas estructuras estáticas, porque durante la
compilación se les asigna un espacio de memoria, y éste permanece inalterable
durante la ejecución del programa. En la siguiente unidad se muestra la estructura de
datos “Lista”. La cual es un tipo de estructura lineal y dinámica de datos. Lineal
debido a que a cada elemento le puede seguir sólo otro elemento, y dinámica porque
se puede manejar la memoria de manera flexible, sin necesidad de reservar espacio
con anticipación.
Una pila (stack en inglés) es una lista ordinal o estructura de datos en la que el
modo de acceso a sus elementos es de tipo LIFO (del inglés Last In First Out, último
en entrar, primero en salir) que permite almacenar y recuperar datos. Se aplica en
multitud de ocasiones en informática debido a su simplicidad y ordenación implícita
en la propia estructura. Para el manejo de los datos se cuenta con dos operaciones
básicas: apilar (push), que coloca un objeto en la pila, y su operación inversa, retirar
(o desapilar, pop), que retira el último elemento apilado. En cada momento sólo se
tiene acceso a la parte superior de la pila, es decir, al último objeto apilado
(denominado TOS, Top of Stack en inglés). La operación retirar permite la obtención
de este elemento, que es retirado de la pila permitiendo el acceso al siguiente (apilado
con anterioridad), que pasa a ser el nuevo TOS. Por analogía con objetos cotidianos,
una operación apilar equivaldría a colocar un plato sobre una pila de platos, y una
operación retirar a retirarlo.
Operaciones
Una pila cuenta con 2 operacines imprescindibles: apilar y desapilar, a las que en
las implementaciones modernas de las pilas se suelen añadir más de uso habitual.
Crear: se crea la pila vacía.
Apilar: se añade un elemento a la pila.(push)
Desapilar: se elimina el elemento frontal de la pila.(pop)
Cima: devuelve el elemento que esta en la cima de la pila. (top o peek)
Vacía: devuelve cierto si la pila está vacía o falso en caso contrario.
crearPila (nombrePila)
Inserción de nodos
push(nombrePila, valorInfo)
Extracción de nodos
pop(nombrePila) → información
Acceso a la cabecera
cabecera(nombrePila) → informacion
info(referenciaNodo) → Informacion
siguiente(referenciaNodo) → Enlace
asignarInfo(referenciaNodo, valorInformacion)
asignarEnlace(referenciaNodo, valorEnlace)
COLAS
Una cola (también llamada fila) es una estructura de datos, caracterizada por ser
una secuencia de elementos en la que la operación de inserción push se realiza por un
extremo y la operación de extracción pop por el otro. También se le llama estructura
FIFO (del inglés First In First Out), debido a que el primer elemento en entrar será
también el primero en salir.
Operaciones
· Crear: se crea cola vacía.
· Encolar (añadir, entrar, insertar): se añade un elemento a la cola. Se
añade al final de esta.
· Desencolar (sacar, salir, eliminar): se elimina el elemento frontal de la
cola, es decir, el primer elemento que entró.
· Frente (consultar, front): se devuelve el elemento frontal de la cola, es
decir, el primer elemento que entró.
Operaciones con Cola
Creación de la cola
crearCola (nombreCola)
ColaVacia(nombreCola) → Booleano
ColaLlena(nombreCola) → Booleano
Inserción de nodos
encolar(nombreCola, valorInfo)
Extracción de nodos
desencolar(nombrePila) → informacion
Acceso a la cabecera
cabecera(nombrePila) → informacion
info(referenciaNodo) → Informacion
siguiente(referenciaNodo) → Enlace
asignarInfo(referenciaNodo, valorInformacion)
asignarEnlace(referenciaNodo, valorEnlace)
Cola Circulares
Una cola circular o anillo es una estructura de datos en la que los elementos están
de forma circular y cada elemento tiene un sucesor y un predecesor. Los elementos
pueden cosultarse, añadirse y eliminarse únicamente desde la cabeza del anillo que es
una posición distinguida. Existen dos operaciones de rotaciones, una en cada sentido,
de manera que la cabeza del anillo pasa a ser el elemento sucesor, o el predecesor,
respectivamente, de la cabeza actual.
Tipos de Colas
a) Colas Circulares (anillos): en las que el último elemento y el primero están
unidos.
b) Colas de Prioridad: En ellas, los elementos se atienden en el orden
indicado por una prioridad asociada a cada uno. Si varios elementos tienen la misma
prioridad, se atenderán de modo convencional según la posición que ocupen. Hay 2
formas de implementación:
a. Añadir un campo a cada nodo con su prioridad. Resulta conveniente
mantener la cola ordenada por orden de prioridad.
b. Crear tantas colas como prioridades haya, y almacenar cada elemento en su
cola.
c) Bicolas: son colas en donde los nodos se pueden añadir y quitar por ambos
extremos; se les llama DEQUE (Double Ended QUEue). Para representar las Bicolas
se puede hacer con un array ciruclar con inicio y fin que apunten a cada uno de los
extremos. Hay variantes:
a. Bicolas de entrada restringida: son aquellas donde la inserción sólo se hace
por el final, aunque se puede eliminar al inicio ó al final.
b. Bicolas de salida restringida: son aquellas donde sólo se elimina por el
final, aunque se puede insertar al inicio y al final.
Aplicaciones de Colas
Las colas también se utilizan en muchas maneras en los sistemas operativos para
planificar el uso de los distintos recursos de la computadora. Uno de estos recursos es
la propia CPU (Unidad Central de Procesamiento).
Si esta trabajando en un sistema multiusuario, cuando le dice a la computadora
que ejecute un programa concreto, el sistema operativo añade su petición a su “cola
de trabajo”. Cuando su petición llega al frente de la cola, el programa solicitado pasa
a ejecutarse. Igualmente, las colas se utilizan para asignar tiempo a los distintos
usuarios de los dispositivos de entrada / salida (E/S), impresoras, discos, cintas y
demás. El sistema operativo mantiene colas para peticiones de imprimir, leer o
escribir en cada uno de estos dispositivos.
Listas. Definición
Tipos de listas
Listas simples
Se definen como un conjunto de nodos uno detrás de otro, del cual siempre se
puede conocer al nodo inicial y al final, de cada nodo de la lista, se conoce un
contenido, que es la información que almacena dentro puede ser de cualquier tipo de
dato un sucesor único excepto el ultimo nodo de la lista.
crearLista (nombreLista)
Inserción de nodos
Borrado de nodos
borrar(nombreLista, valorInfo)
Búsqueda de un nodo
buscar(nombreLista, dato)→información
buscar(nombreLista, dato)→referenciaNodo
pertenece(nombreLista,informacion) → Booleano
Recorrido de la lista
recorrer(nombreLista)
info(referenciaNodo) → Informacion
siguiente(referenciaNodo) → enlace
Modificación de nodos
asignarInfo(referenciaNodo, valorInformacion)
asignarEnlace(referenciaNodo, valorEnlace)
Listas ordenadas
Son las que la posición de cada nodo viene determinada por el valor de uno o
más campos obligatorios de información del nodo denominados clave No se permite
tener dos nodos con la misma clave.
crearLista (nombreLista)
listaVacia(nombreLista) → Booleano
listaVacia(referenciaNodo) → Booleano
listaLlena(nombreLista) → Booleano
Insesrción de nodo
Borrado de nodos
borrar(nombreLista, valorClave)
Búsqueda de un nodo
clave(referenciaNodo) → Clave
info(referenciaNodo) → Informacion
siguiente(referenciaNodo) → enlace
La lista enlazada es un TDA que nos permite almacenar datos de una forma
organizada, al igual que los vectores pero, a diferencia de estos, esta estructura es
dinámica, por lo que no tenemos que saber "a priori" los elementos que puede
contener.
En una lista enlazada, cada elemento apunta al siguiente excepto el último que no
tiene sucesor y el valor del enlace es null. Por ello los elementos son registros que
contienen el dato a almacenar y un enlace al siguiente elemento. Los elementos de
una lista, suelen recibir también el nombre de nodos de la lista.
struct lista {
gint dato;
lista *siguiente;
};
Después de esta breve introducción, que sólo pretende servir como recordatorio,
pasaremos a ver cómo es la estructura GSList que, junto con el conjunto de funciones
que la acompañan, forman el TDA lista enlazada en GLib™.
GSList
struct GSList {
gpointer data;
GSList *next;
};
GINT_TO_POINTER ()
GPOINTER_TO_INT ()
GUINT_TO_POINTER ()
GPOINTER_TO_UINT ()
Más adelante, en esta misma sección, se verán ejemplos del uso de estas macros.
GCompareFunc es una función que se utiliza para comparar dos valores y saber
así cual de ellos hay que insertar primero. En los dos ejemplos que hay a
continuación, se puede observar una función de tipo GCompareFunc y su uso para
insertar datos en una lista en orden creciente.
Las dos funciones expuestas para la eliminación de nodos, si bien tienen una
definición prácticamente idéntica, el resultado obtenido es distinto. En el caso
de g_slist_remove, se eliminará el nodo que contenga el valor data. Si hay varios
nodos con el mismo valor, sólo se eliminará el primero. Si ningún nodo contiene ese
valor, no se realiza ningún cambio en el GSList. En el caso de g_slist_remove_all, se
eliminan todos los nodos de la lista que contengan el valor data y nos devuelve la
nueva lista resultante de la eliminación de los nodos.
La función g_slist_next, es una macro que nos devuelve el siguiente nodo. Esta
macro la podemos utilizar para recorrer la lista.
gint i = 0;
Funciones asociadas
Operador
a GSList.
Con estas funciones, quedan definidos los operadores básicos del TDA lista
enlazada. GSList trae otras funciones además de los operadores básicos. Para más
información sobre estas, está disponible el manual de referencia de GLib™.
Ejemplo 1
public class C1
{
private C2 atr;
Suponga además que dentro del método m1() se invoca el método m2() de
la clase C2 sobre el atributo llamado atr.
public class C2
{
public void m2( )
{
instr1;
instr2;
instr3;
}
}
Dentro de la clase C2 hay un método llamado m2() que tiene 3
instrucciones, que aquí mostramos como instr1, instr2, instr3. Dichas instrucciones
pueden ser de cualquier tipo.
Suponga que se está ejecutando la instrucción instr2 del método m2() y se
produce un error. En ese momento, a causa del problema el computador decide que
no puede seguir con la ejecución del método (instr3 no se va a ejecutar).
Crea entonces un objeto de la clase Exception que dice que el error sucedió
en la instrucción instr2 del método m2() y explica la razón del problema.
Luego, pasa dicho objeto al método m1() de la clase C1, que fue quien hizo
la llamada. Si él lo atrapa (ya veremos más adelante cómo), el computador
continúa la ejecución en el punto que dicho método indique.
Si el método m1() no atrapa la excepción, este objeto pasa a la clase de la
interfaz que hizo la llamada. Este proceso se repite hasta que alguien atrape
la excepción o hasta que el programa completo se detenga. Entendemos por
manejar una excepción el hecho de poderla identificar, atraparla antes de que el
programa deje de funcionar y realizar una acción para recuperarse del error (por lo
menos, para informarle al usuario lo sucedido de manera amigable y no con un
mensaje poco comprensible del computador).
Multithreading
Visión general
El paradigma de subprocesos múltiples se ha vuelto más popular a medida que
los esfuerzos para explotar aún más el paralelismo a nivel de instrucción se han
estancado desde finales de los años noventa. Esto permitió que el concepto de
computación de rendimiento resurgiera del campo más especializado del
procesamiento de transacciones. A pesar de que es muy difícil acelerar un solo
subproceso o un solo programa, la mayoría de los sistemas informáticos son en
realidad multitarea entre varios subprocesos o programas. Por lo tanto, las técnicas
que mejoran el rendimiento de todas las tareas resultan en ganancias de rendimiento
general..
Ventajas
Si un subproceso obtiene una gran cantidad de errores de caché, los otros
subprocesos pueden continuar aprovechando los recursos informáticos no utilizados,
lo que puede llevar a una ejecución general más rápida, ya que estos recursos estarían
inactivos si solo se ejecutara un único subproceso. Además, si un subproceso no
puede utilizar todos los recursos informáticos de la CPU (porque las instrucciones
dependen del resultado de cada uno), ejecutar otro subproceso puede evitar que esos
recursos se vuelvan inactivos.
Desventajas
Varios subprocesos pueden interferir entre sí cuando se comparten recursos de
hardware, como cachés o buffers de traducción (TLB). Como resultado, los tiempos
de ejecución de un solo hilo no se mejoran y se pueden degradar, incluso cuando solo
se está ejecutando un hilo, debido a las frecuencias más bajas o etapas de canalización
adicionales que son necesarias para acomodar el hardware de conmutación de hilos.
La eficiencia general varía; Intel reclama una mejora de hasta un 30% con su
tecnología Hyper-Threading, [1] mientras que un programa sintético que simplemente
realiza un ciclo de operaciones de punto flotante dependientes no optimizadas en
realidad gana una mejora de velocidad del 100% cuando se ejecuta en paralelo. Por
otro lado, los programas de lenguaje ensamblador ajustados a mano que utilizan las
extensiones MMX o AltiVec y realizan la búsqueda previa de datos (como un buen
codificador de video) no sufren pérdidas de caché o recursos informáticos inactivos.
Por lo tanto, tales programas no se benefician de los subprocesos múltiples de
hardware y, de hecho, pueden ver un rendimiento degradado debido a la disputa por
los recursos compartidos.
Tipos de multihilo
Por ejemplo:
Entrelazado multihilo
Bibliografía