Programación de estructuras complejas

Anuncio
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
Descargar