REPÚBLICA BOLIVARIANA DE VENEZUELA MINISTERIO DEL PODER POPULAR PARA LA DEFENSA UNIVERSIDAD NACIONAL EXPERIMENTAL POLITÉCNICADE LA FUERZA ARMADA NACIONAL NUCLEO- FALCON Tipos De Datos Complejos Realizado Por: Alexander Oliva 21.112.327 Luis Parra 19.866.529 Tito Deli 24.351.188 Angel García 23.678.440 Moises Siritt 17.925.758 Jesus Cuauro 23.674.035 Jeizer Medina 21.430.528 IS3D-C pág. 1 Índice Tipos De Datos Complejos PILA: COLAS: Listas Grafos ÁRBOLES Ejemplos de estructuras complejas: Pilas: Listas: Arboles: Colas: Conclusión Bibliografía PAG 4 4 11 12 27 29 32 32 33 37 39 40 41 pág. 2 Introducción Existen diversos tipos de datos simples, los cuales por separados cumplen tareas sencillas, sin embargo hay casos en los que se precisa manejar gran cantidad de datos, que además deben ser ordenados y almacenar mas de un tipo de dato, estos se conocen como tipos de datos “complejos” porque pueden almacenar cualquier tipo de dato, además cuentan con formas especiales de ordenamiento que según su implementación los hace buenas opciones cuando se deben realizar las tareas de consulta muchas veces. pág. 3 Tipos De Datos Complejos Son la agrupación de varios de datos simples, no necesariamente del mismo tipo, que se maneja como un solo ente como: registros, listas (pilas, colas, arboles, grafos) y archivos. Un valor complejo (o tipo de datos complejo) es un valor que no es simple y que hace referencia a los valores simples. Estos se denominan con frecuencia tipos de datos de referencia. Los valores complejos pertenecen al tipo de datos Object (objeto) o a un tipo de datos basado en el tipo de datos Object. Entre los tipos de datos que definen conjuntos de valores complejos se encuentran Array (Vector, matriz), Date (fecha en enteros), entre otros. PILA: La pila es una estructura de datos que permite almacenar datos en el orden LIFO (Last In First Out) en español, último en entrar, primero en salir). La recuperación de los datos es hecha en el orden inverso de su inserción. Para la implementación hemos elegido una lista enlazada simple, presentada sobre la vertical. Ya que la inserción es siempre hecha al inicio de la lista, el 1er elemento de la lista será el ultimo elemento ingresado, por lo tanto estará en la cabeza de la pila. No hemos utilizado un puntero fin, como en el caso de la lista enlazada simple, ya que el objetivo no es el de tratar una lista enlazada, sino una pila. Lo interesante es que el ultimo elemento ingresado, será el 1er elemento recuperado. pág. 4 La construcción del modelo de un elemento de la pila Para definir un elemento de la pila será utilizado el tipo struct. El elemento de la pila contendrá un campo dato y un puntero siguiente. El puntero siguiente debe ser del mismo tipo que el elemento, de lo contrario no podrá apuntar hacia el elemento. El puntero siguiente permitirá el acceso al próximo elemento. typedef struct ElementoLista { char *dato; struct ElementoLista *siguiente; }Elemento; Para permitir las operaciones sobre la pila, vamos a guardar ciertos elementos: El primer elemento El numero de elementos El 1er elemento, que se encuentra en la cabeza de la pila, nos permitirá realizar la operación de recuperación de los datos situados en la parte superior de la pila. Para ello, será utilizada otra estructura (no es obligatorio, pueden ser utilizadas variables). typedef struct ListaUbicación{ Elemento *inicio; int tamaño; } Pila; El puntero inicio contendrá la dirección del 1er elemento de la lista. La variable tamaño contiene el número de elementos. Nota: Esta vez no utilizamos un puntero fin (ver la lista enlazada simple), no lo necesitamos puesto que únicamente trabajamos al inicio de la lista. Cualquiera que sea la posición en la lista, el puntero inicio apunta siempre hacia el 1er elemento, que estará en la cabeza de la pila. El campo tamaño contendrá el número de elementos de la pila, cualquiera que sea la operación efectuada sobre la pila. pág. 5 Operaciones sobre las pilas A. Inicialización Modelo de la función: void inicialización (Pila *tas); Esta operación debe ser hecha antes de cualquier otra operación sobre la pila. Esta inicializa el puntero inicio con el puntero NULL, y el tamaño con el valor 0. La función void inicialización (Pila * tas){ tas->inicio = NULL; tas->tamaño = 0; } B. Inserción de un elemento en la pila A continuación el algoritmo de inserción y registro de los elementos: declaración del elemento a insertar asignación de la memoria para el nuevo elemento rellenar el contenido del campo de datos actualizar el puntero inicio hacia el 1er elemento (la cabeza de la pila) Actualizar el tamaño de la pila. Modelo de la función: int apilar (Pila *tas, char *dato); La primera imagen muestra el inicio de la inserción, por lo tanto la lista de tamaño 1 después de la inserción. La característica de la pila no es muy apreciada con un solo elemento, ya que es el único a recuperar. pág. 6 En cambio la 2da imagen nos permite observar el comportamiento de la pila. Lo que debemos retener es que la inserción siempre se hace en la parte superior de la pila (al inicio de la lista). pág. 7 La función /* apilar (añadir) un elemento en la pila */ int apilar (Pila * tas, char *dato){ Elemento *nuevo_elemento; if ((nuevo_elemento = (Elemento *) malloc (sizeof (Elemento))) == NULL) return -1; if ((nuevo_elemento->dato = (char *) malloc (50 * sizeof (char))) == NULL) return -1; strcpy (nuevo_elemento->dato, dato); nuevo_elemento->siguiente = tas->inicio; tas->inicio = nuevo_elemento; tas->tamaño++; } C. Eliminar un elemento de la pila Para eliminar un elemento de la pila, simplemente hay que eliminar el elemento hacia el cual apunta el puntero inicio. Esta operación no permite recuperar el dato en la cabeza de la pila, solo eliminarlo. Modelo de la función: int desapilar (Pila *tas); La función devuelve -1 en caso de error, si no devuelve 0. Las etapas: el puntero sup_elemento contendrá la dirección del 1er elemento el puntero inicio apuntará hacia el 2do elemento (después de la eliminación del 1er elemento, el 2do elemento estará en la cabeza de la pila) el tamaño de la pila disminuirá un elemento. pág. 8 La función int desapilar (Pila * tas){ Elemento *sup_elemento; if (tas->tamaño == 0) return -1; sup_elemento = tas->inicio; tas->inicio = tas->inicio->siguiente; free (sup_elemento->dato); free (sup_elemento); tas->tamaño--; return 0; } D. Visualización de la pila Para mostrar la pila entera, es necesario posicionarse al inicio de la pila (el puntero inicio lo permitirá). Luego, utilizando el puntero siguiente de cada elemento, la pila es recorrida del 1er hacia el último elemento. La condición para detenerse es dada por el tamaño de la pila. La función /* visualización de la pila */ pág. 9 void muestra (Pila * tas){ Elemento *actual; int i; actual = tas->inicio; for(i=0;i<tas->tamaño;++i){ printf("\t\t%s\n", actual->dato); actual = actual->siguiente; } } E. Recuperación del dato en la cabeza de la pila Para recuperar el dato en la cabeza de la pila sin eliminarlo, he utilizado una macro. La macro lee los datos en la parte superior de la pila utilizando el puntero inicio. #define pila_dato(tas) tas->inicio->dato En la vida cotidiana existen muchos ejemplos de pilas, una pila de platos en una alacena, una pila de latas en un supermercado, una pila de papeles sobre un escritorio, etc. Su utilización principal es para el software del sistema, compiladores e intérpretes. Representación en Memoria Las pilas no son estructuras de datos fundamentales, es decir, no están definidas como tales en los lenguajes de programación. Las pilas pueden representarse mediante el uso de: Arreglos. Listas enlazadas. Nosotros ahora usaremos los arreglos. Por lo tanto debemos definir el tamaño máximo de la pila, además de un apuntador al último elemento insertado en la pila el cual denominaremos SP. Una representación gráfica de una pila es la siguiente: pág. 10 COLAS: Son listas lineales de información a las cuales se accede de un modo determinado siendo el de tipo FIFO (First In, First Out) (Primero en Entrar, Primero en Salir), lo que quiere decir que el primer dato en entrar es también el primer dato en salir, en las colas no se permite el acceso aleatorio a ningún elemento concreto. Las recuperaciones de operaciones son destructivas (elimina el elemento) de la cola, si no es almacenado en otro lugar se destruye. Su utilización principal en las simulaciones, planificación de sucesos, y los procesos de entrada y salida con buffer. Existen muchísimos ejemplos de colas en la vida real, como por ejemplo: personas esperando en un teléfono público, niños esperando para subir a un juego mecánico, estudiantes esperando para subir a un camión escolar, etc. Representación en Memoria Podemos representar a las colas de dos formas: Como arreglos Como listas ordenadas En lo sucesivo, al apuntador del primer elemento lo llamaremos F, al último elemento A y MAXIMO para definir el número máximo de elementos en la cola. pág. 11 Cola Lineal La cola lineal es un tipo de almacenamiento creado por el usuario que trabaja bajo la técnica FIFO (primero en entrar primero en salir). Las colas lineales se representan gráficamente de la siguiente manera: Las condiciones a considerar en el tratamiento de colas lineales son las siguientes: · Overflow (cola llena), cuando se realice una inserción. · Underflow(cola vacía), cuando se requiera de una extracción en la cola. · Vacío Listas Son colecciones de elementos, donde cada uno de ellos, además de almacenar información, almacena la dirección del siguiente elemento. Una lista es una estructura lineal de datos. Es decir, cada uno de sus componentes y un predecesor únicos, con excepción del último y del primero. Las listas pueden implementarse mediante arreglos, resultando así una estructura estática. También puede ser una memoria dinámica (La cantidad de memoria ocupada puede modificarse durante la ejecución del programa). Una lista enlazada o encadenada es una colección de elementos ó nodos, en donde cada uno contiene datos y un enlace o liga. Un nodo es una secuencia de caracteres en memoria dividida en campos (de cualquier tipo). Un nodo siempre contiene la dirección de memoria del siguiente nodo de información si este existe. Un apuntador es la dirección de memoria de un nodo La figura siguiente muestra la estructura de un nodo: pág. 12 El campo liga, que es de tipo puntero, es el que se usa para establecer la liga con el siguiente nodo de la lista. Si el nodo fuera el último, este campo recibe como valor NIL(vacío). A continuación se muestra el esquema de una lista: Tipos de Listas Enlazadas 1. Listas simples enlazadas La lista enlazada básica es la lista enlazada simple la cual tiene un enlace por nodo. Este enlace apunta al siguiente nodo en la lista, o al valor NULL o a la lista vacía, si es el último nodo. Una lista enlazada simple contiene dos valores: el valor actual del nodo y un enlace al siguiente nodo Las listas enlazadas son estructuras de datos semejantes a los array salvo que el acceso a un elemento no se hace mediante un indice sino mediante un puntero. La asignación de memoria es hecha durante la ejecución. En una lista los elementos son contiguos en lo que concierne al enlazado. En cambio, mientras que en un array los elementos están contiguos en la memoria, en una lista los elementos están dispersos. El enlace entre los elementos se hace mediante un puntero. pág. 13 En realidad, en la memoria la representación es aleatoria en función del espacio asignado. El puntero siguiente del último elemento debe apuntar hacia NULL (el fin de la lista). Para acceder a un elemento, la lista es recorrida comenzando por el inicio, el puntero siguiente permite el desplazamiento hacia el próximo elemento. El desplazamiento se hace en una sola dirección, del primer al último elemento. Si deseas desplazarte en las dos direcciones (hacia delante y hacia atrás) deberás utilizar las listas doblemente enlazadas. Construcción del modelo de un elemento de la lista Para definir un elemento de la lista, será utilizado el tipo struct. El elemento de la lista contendrá un campo dato y un puntero siguiente. El puntero siguiente debe ser del mismo tipo que el elemento, si no, no podrá apuntar hacia el elemento. El puntero siguiente permitirá el acceso al próximo elemento. typedef struct ElementoLista { char *dato; struct ElementoLista *siguiente; }Elemento; Para tener el control de la lista es preferible guardar ciertos elementos: El primer elemento, el último elemento, el número de elementos. Para ello será utilizado otra estructura (no es obligatorio, pueden ser utilizadas variables) typedef struct ListaIdentificar { Elemento *inicio; Elemento *fin; int tamaño; }Lista; El puntero inicio contendrá la dirección del primer elemento de la lista. El puntero fin contendrá la dirección del último elemento de la lista. La variable tamaño contiene el número de elementos. Cualquiera que sea la posición en la lista, los punteros inicio y fin apuntan siempre al primer y último elemento. El campo tamaño contendrá el número de elementos de la lista cualquiera que sea la operación efectuada sobre la lista. pág. 14 Operaciones sobre las listas enlazadas Para la inserción y la eliminación, una solo función bastará si está bien concebida de acuerdo a lo que se necesite. Debo recordar que este artículo es puramente didáctico. Por esta razón he escrito una función para cada operación de inserción y eliminación. A. Inicialización Modelo de la función: void inicializacion (Lista *lista); Esta operación debe ser hecha antes de cualquier otra operación sobre la lista. Esta inicializa el puntero inicio y el puntero fin con el puntero NULL, y el tamaño con el valor 0. La función void inicializacion (Lista *lista){ lista->inicio = NULL; lista->fin = NULL; tamaño = 0; } B. Inserción de un elemento en la lista A continuación el algoritmo de inserción y registro de los elementos: declaración del elemento a insertar asignación de la memoria para el nuevo elemento rellenar el contenido del campo de datos actualizar los punteros hacia el primer y ultimo elemento si es necesario. o Caso particular: en una lista con un solo elemento, el primero es al mismo tiempo el último. o Actualizar el tamaño de la lista Para añadir un elemento a la lista hay varios casos: 1. Inserción en una lista vacía 2. Inserción al inicio de la lista 3. Inserción al final de la lista 4. Inserción en otra parte de la lista. pág. 15 1. Inserción en una lista vacía Ejemplo de la función: int ins_en_lista_vacia (Lista *lista, char *dato); La función devuelve 1 en caso de error, si no devuelve 0. Etapas: asignación de memoria para el nuevo elemento rellenar el campo de datos del nuevo elemento el puntero siguiente del nuevo elemento apuntará hacia NULL (ya que la inserción es hecha en una lista vacía se utiliza la dirección del puntero inicio que vale NULL) los punteros inicio y fin apuntaran hacia el nuevo elemento el tamaño es actualizado La función /* inserción en una lista vacía */ int ins_en_lista_vacia (Lista * lista, char *dato){ Element *nuevo_elemento; if ((nuevo_elemento = (Element *) malloc (sizeof (Element))) == NULL) return -1; if ((nuevo _elemento->dato = (char *) malloc (50 * sizeof (char))) == NULL) return -1; pág. 16 strcpy (nuevo_elemento->dato, dato); nuevo_elemento->siguiente = NULL; lista->inicio = nuevo_elemento; lista->fin = nuevo_elemento; lista->tamaño++; return 0; } 2. Inserción al inicio de la lista Ejemplo de la función: int ins_inicio_lista (Lista *lista,char *dato); La función devuelve -1 en caso de error, si no devuelve 0. Etapas: asignación de memoria al nuevo elemento rellenar el campo de datos del nuevo elemento el puntero siguiente del nuevo elemento apunta hacia el primer elemento el puntero inicio apunta hacia el nuevo elemento el puntero fin no cambia el tamaño es incrementado pág. 17 La función /* inserción al inicio de la lista */ int ins_inicio_lista (Lista * lista, char *dato){ Element *nuevo_elemento; if ((nuevo_elemento = (Element *) malloc (sizeof (Element))) == NULL) return -1; if ((nuevo_elemento->dato = (char *) malloc (50 * sizeof (char))) == NULL) return -1; strcpy (nuevo_elemento->dato, dato); nuevo_elemento->siguiente = lista->inicio lista->inicio = nuevo_elemento; lista->tamaño++; return 0; } 3. Inserción al final de la lista Ejemplo de la función: int ins_fin_lista (Lista *lista, Element *actual, char *dato); La función devuelve -1 en caso de error, si no devuelve 0. Etapas: Asignación de memoria al nuevo elemento Rellenar el campo de datos del nuevo elemento El puntero siguiente del ultimo elemento apunta hacia el nuevo elemento El puntero fin apunta hacia el nuevo elemento El puntero inicio no cambia El tamaño es incrementado pág. 18 La función /*inserción al final de la lista */ int ins_fin_lista (Lista * lista, Element * actual, char *dato){ Element *nuevo_elemento; if ((nuevo_elemento = (Element *) malloc (sizeof (Element))) == NULL) return -1; if ((nuevo_elemento->dato = (char *) malloc (50 * sizeof (char))) == NULL) return -1; strcpy (nuevo_elemento->dato, dato); actual->siguiente = nuevo_elemento; nuevo_elemento->siguiente = NULL; lista->fin = nuevo_elemento; lista->tamaño++; return 0; } 4. Inserción en otra parte de la lista Ejemplo de la función: int ins_lista (Lista *lista, char *dato,int pos); La función devuelve -1 en caso de error, si no devuelve 0. La inserción se efectuará después de haber pasado a la función una posición como argumento. Si la posición indicada no tiene que ser el último elemento. En ese caso hay que utilizar la función de inserción al final de la lista. Etapas: asignación de memoria al nuevo elemento rellenar el campo de datos del nuevo elemento Elegir una posición en la lista (la inserción se hará después de haber elegido la posición) el puntero siguiente del nuevo elemento apunta hacia la dirección a la que apunta el puntero siguiente del elemento actual. el puntero siguiente del elemento actual apunta al nuevo elemento los punteros inicio y fin no cambian el tamaño se incrementa en una unidad pág. 19 La función /* inserción en la posición solicitada */ int ins_lista (Lista * lista, char *dato, int pos){ if (lista->tamaño < 2) return -1; if (pos < 1 || pos >= lista->tamaño) return -1; Element *actual; Element *nuevo_elemento; int i; if ((nuevo_elemento = (Element *) malloc (sizeof (Element))) == NULL) return -1; if ((nuevo_elemento->dato = (char *) malloc (50 * sizeof (char))) == NULL) return -1; actual = lista->inicio; for (i = 1; i < pos; ++i) actual = actual->siguiente; if (actual->siguiente == NULL) return -1; pág. 20 strcpy (nuevo_elemento->dato, dato); nuevo_elemento->siguiente = actual->siguiente; actual->siguiente = nuevo_elemento; lista->tamaño++; return 0; C. Eliminación de un elemento de la lista A continuación un algoritmo para eliminar un elemento de la lista: uso de un puntero temporal para guardar la dirección de los elementos a eliminar el elemento a eliminar se encuentra después del elemento actual Apuntar el puntero siguiente del elemento actual hacia la dirección del puntero siguiente del elemento a eliminar liberar la memoria ocupada por el elemento eliminado actualizar el tamaño de la lista Para eliminar un elemento de la lista hay varios casos: 1. Eliminación al inicio de la lista 2. Eliminación en otra parte de la lista 1. Eliminación al inicio de la lista Ejemplo de la función: int sup_inicio (Lista *lista); La función devuelve -1 en caso de error, si no devuelve 0. Etapas: el puntero sup_elem contendrá la dirección del 1er elemento el puntero inicio apuntara hacia el 2do elemento el tamaño de la lista disminuirá un elemento pág. 21 La función /* eliminación al inicio de la lista */ int sup_inicio (Lista * lista){ if (lista->tamaño == 0) return -1; Element *sup_elemento; sup_element = lista->inicio; lista->inicio = lista->inicio->siguiente; if (lista->tamaño == 1) lista->fin = NULL; free (sup_elemento->dato); free (sup_elemento); lista->tamaño--; return 0; } 2. Eliminación en otra parte de la lista Ejemplo de la función: int sup_en_lista (Lista *lista, int pos); La función devuelve -1 en caso de error, si no devuelve 0. Etapas: pág. 22 el puntero sup_elem contendrá la dirección hacia la que apunta el puntero siguiente del elemento actual el puntero siguiente del elemento actual apuntara hacia el elemento al que apunta el puntero siguiente del elemento que sigue al elemento actual en la lista. Si el elemento actual es el penúltimo elemento, el puntero fin debe ser actualizado. El tamaño de la lista será disminuido en un elemento pág. 23 La función /* eliminar un elemento después de la posición solicitada */ int sup_en_lista (Lista * lista, int pos){ if (lista->tamaño <= 1 || pos < 1 || pos >= lista->tamaño) return -1; int i; Element *actual; Element *sup_elemento; actual = lista->inicio; for (i = 1; i < pos; ++i) actual = actual->siguiente; sup_elemento = actual->siguiente; actual->siguiente = actual->siguiente->siguiente; if(actual->siguiente == NULL) lista->fin = actual; free (sup_elemento->dato); free (sup_elemento); lista->tamaño--; return 0; } D. Visualización de la lista Para mostrar la lista entera hay que posicionarse al inicio de la lista (el puntero inicio lo permitirá). Luego utilizando el puntero siguiente de cada elemento la lista es recorrida del 1ero al ultimo elemento. La condición para detener es dada por el puntero siguiente del ultimo elemento que vale NULL. La función /* visualización de la lista */ void visualización (Lista * lista){ Element *actual; actual = lista->inicio; while (actual != NULL){ printf ("%p - %s\n", actual, actual->dato); actual = actual->siguiente; } } E. Destrucción de la lista Para destruir la lista entera, he utilizado la eliminación al inicio de la lista ya que el tamaño es mayor a cero. pág. 24 La función /* destruir la lista */ void destruir (Lista * lista){ while (lista->tamaño > 0) sup_inicio (lista); } 2. Lista Doblemente Enlazada Un tipo de lista enlazada más sofisticado es la lista doblemente enlazada o lista enlazadas de dos vías. Cada nodo tiene dos enlaces: uno apunta al nodo anterior, o apunta al valor NULL si es el primer nodo; y otro que apunta al nodo siguiente, o apunta al valor NULL si es el último nodo. Una lista doblemente enlazada contiene tres valores: el valor, el link al nodo siguiente, y el link al anterior En algún lenguaje de muy bajo nivel, XOR-Linking ofrece una vía para implementar listas doblemente enlazadas, usando una sola palabra para ambos enlaces, aunque el uso de esta técnica no se suele utilizar. 3. Listas enlazadas circulares En una lista enlazada circular, el primer y el último nodo están unidos juntos. Esto se puede hacer tanto para listas enlazadas simples como para las doblemente enlazadas. Para recorrer una lista enlazada circular podemos empezar por cualquier nodo y seguir la lista en cualquier dirección hasta que se regrese hasta el nodo original. Desde otro punto de vista, las listas enlazadas circulares pueden ser vistas como listas sin comienzo ni fin. Este tipo de listas es el más usado para dirigir buffers para “ingerir” datos, y para visitar todos los nodos de una lista a partir de uno dado. Una lista enlazada circular que contiene tres valores enteros Listas enlazadas circulares simples Cada nodo tiene un enlace, similar al de las listas enlazadas simples, excepto que el siguiente nodo del último apunta al primero. Como en una lista enlazada simple, los nuevos nodos pueden ser solo eficientemente insertados después de uno que ya tengamos referenciado. Por esta razón, es usual quedarse con una referencia solamente al último pág. 25 elemento en una lista enlazada circular simple, esto nos permite rápidas inserciones al principio, y también permite accesos al primer nodo desde el puntero del último nodo. 1 Lista Enlazada Doblemente Circular En una lista enlazada doblemente circular, cada nodo tiene dos enlaces, similares a los de la lista doblemente enlazada, excepto que el enlace anterior del primer nodo apunta al último y el enlace siguiente del último nodo, apunta al primero. Como en una lista doblemente enlazada, las inserciones y eliminaciones pueden ser hechas desde cualquier punto con acceso a algún nodo cercano. Aunque estructuralmente una lista circular doblemente enlazada no tiene ni principio ni fin, un puntero de acceso externo puede establecer el nodo apuntado que está en la cabeza o al nodo cola, y así mantener el orden tan bien como en una lista doblemente enlazada. Nodos Centinelas A veces las listas enlazadas tienen un nodo centinela (también llamado falso nodo o nodo ficticio) al principio o al final de la lista, el cual no es usado para guardar datos. Su propósito es simplificar o agilizar algunas operaciones, asegurando que cualquier nodo tiene otro anterior o posterior, y que toda la lista (incluso alguna que no contenga datos) siempre tenga un “primer y último” nodo. Grafos Un grafo es un conjunto de puntos (vértices) en el espacio, que están conectados por un conjunto de líneas (aristas). Los grafos son estructuras de datos dinámicas no lineales, utilizadas comúnmente en el análisis de redes, en diseño de circuitos eléctricos, en estrategias de mercados, cartografía, mapas conceptuales, matemáticas, planificación de procesos y muchas área del conocimiento. Conceptos Básicos: Aristas Son las líneas con las que se unen las aristas de un grafo y con la que se construyen también caminos. pág. 26 Si la arista carece de dirección se denota indistintamente {a, b} o {b, a}, siendo a y b los vértices que une. Si {a ,b} es una arista, a los vértices a y b se les llama sus extremos. Aristas Adyacentes: Se dice que dos aristas son adyacentes si convergen en el mismo vértice. Aristas Paralelas: Se dice que dos aristas son paralelas si vértice inicial y el final son el mismo. Aristas Cíclicas: Arista que parte de un vértice para entrar en el mismo. Cruce: Son dos aristas que cruzan en un punto. Vértices Son los puntos o nodos con los que esta conformado un grafo. Llamaremos grado de un vértice al número de aristas de las que es extremo. Se dice que un vértice es `par' o `impar' según lo sea su grado. Vértices Adyacentes: si tenemos un par de vértices de un grafo (U, V) y si tenemos un arista que los une, entonces U y V son vértices adyacentes y se dice que U es el vértice inicial y V el vértice adyacente. Vértice Aislado: Es un vértice de grado cero. Vértice Terminal: Es un vértice de grado 1. Caminos Sean x, y " V, se dice que hay un camino en G de x a y si existe una sucesión finita no vacía de aristas {x,v1}, {v1,v2},..., {vn,y}. En este caso x e y se llaman los extremos del camino El número de aristas del camino se llama la longitud del camino. Si los vértices no se repiten el camino se dice propio o simple. Si hay un camino no simple entre 2 vértices, también habrá un camino simple entre ellos. Cuando los dos extremos de un camino son iguales, el camino se llama circuito o camino cerrado. Llamaremos ciclo a un circuito simple Un vértice a se dice accesible desde el vértice b si existe un camino entre ellos. Todo vértice es accesible respecto a si mismo pág. 27 ÁRBOLES Es una estructura de datos formada por nodos los cuales están conectados por aristas. Un árbol se define como un tipo de grafo que no contiene ciclos Conceptos Básicos: Árbol Vacío: Un árbol puede estar vacío; es decir no contener ningún nodo. Raíz: es el nodo que está al tope del árbol. Un árbol solo tiene una raíz. pág. 28 Camino: es la secuencia de nodos que hay que visitar para llegar de un nodo a otro de un árbol. Ejemplo: B-A-C-F es el camino entre B y F. Un conjunto de nodos y aristas se define como un árbol si y solo si existe un único camino desde la raíz hasta cada uno de sus nodos. pág. 29 Padre: En un árbol toda rama va de un nodo n1 a un nodo n2, se dice que n1 es padre de n2. Ejemplo: C es padre de E y de F, D es padre de G, de H y de I. Hijo: todo nodo puede tener más de una arista que lo lleva a otro nodo por debajo de él. Estos nodos que se encuentran por debajo de un nodo dado se llaman hijos. Ejemplo: E es hijo de C, B es hijo de A, H es hijo de D Hojas: son aquellos nodos que no tienen hijos. En un árbol solo puede haber una raíz pero puede haber muchas hojas. Ejemplo: B, E, F, G, H, I son hojas. Subárbol: Cualquier nodo se puede considerar como la raíz de un subárbol. pág. 30 Ejemplos de estructuras complejas: Pilas: pág. 31 Listas: pág. 32 pág. 33 pág. 34 pág. 35 Arboles: pág. 36 PROGRAMA ELABORADO EN COMPILADOR: TURBO C++ VERSION 3.0 BORLAND INTERNATIONAL CODIGO FUENTE POR: DANIEL ALBERTO RIVERA PERALES. MATRICULA: 185154 GRUPO: 4§K pág. 37 Colas: pág. 38 Conclusión El uso de los tipos de estructura complejas hacen bastante fácil la programación para las bases de datos como listas de empleados, control de libros en una biblioteca, en banca electrónica, administración de correos online, entre otros, por su capacidad de reunir varios datos en una sola estructura de fácil implementación, dinámica (pueden crecer y decrecer en tiempo de ejecución) y robusta frente a los errores, con capacidades de alto rendimiento en el acceso a la información y a la velocidad a la que se ejecutan los calculo, por ejemplo, una base de datos de consultas recurrente se programaría usando una estructura tipo árbol (que son de orden log n )y así esta tendrá un tiempo de respuesta muy rápido en comparación a un arreglo o una matriz (que son de orden n y nxm respectivamente), permitiendo así programas mas rápidos, con menor uso de recursos y de fácil manipulación. pág. 39 Bibliografía Links: http://ncastillo_unefai.lacoctelera.net/post/2010/06/08/unidad-9-datos-complejos http://es.wikipedia.org/wiki/Lista_%28estructura_de_datos%29 http://www.elrincondelc.com/nuevorincon/index.php?pag=codigos&id=4 http://es.kioskea.net/faq/2885-las-pilas-en-lenguaje-c http://es.kioskea.net/faq/2842-la-lista-enlazada-simple http://es.kioskea.net/faq/2872-listas-doblemente-enlazadas Guia: Universidad Central de Venezuela. Escuela de Computación - Algoritmos y Programación Recopilación y Preparación Prof. Yusneyi Carballo Feb-05, Enero-07, Junio 07 pág. 40