Académique Documents
Professionnel Documents
Culture Documents
TEXTO GUIA:
ESTRUCTURA DE DATOS
SIS2204
Oruro - Bolivia
1
2
CONTENIDO
INTRODUCCION........................................................................................................................6
1.1 ESTRUCTURA DE DATOS. ............................................................................................................................................6
PILAS Y COLAS.......................................................................................................................10
2.1 PILAS. ...............................................................................................................................................................................10
..................................................................................................................................................................................................16
TRATAMIENTO DE ARCHIVOS..............................................................................................30
4.1 INTRODUCCION ............................................................................................................................................................30
3
LISTAS ENCADENADAS........................................................................................................52
5.1 LISTAS ENCADENADAS ..............................................................................................................................................52
TIPOS DE LISTAS. ............................................................................................................................................................52
ÁRBOLES.................................................................................................................................78
6.1 DEFINICION. ..................................................................................................................................................................78
REPRESENTACIÓN ..........................................................................................................................................................78
MATRIZ DE ADYACENCIA ............................................................................................................................................79
LISTA DE ADYACENCIA ................................................................................................................................................79
ESTRUCTURA DINÁMICA PURA ..................................................................................................................................79
4
ÁRBOLES BINARIOS DE EXPRESIONES.....................................................................................................................113
GRAFOS.................................................................................................................................115
7.1 DEFINICIONES. ............................................................................................................................................................115
5
TEMA 1
INTRODUCCION
OBJETIVOS
Conceptuar las diferentes etapas por las que atraviesa el procesamiento de datos desde la abstracción de
los mismos hasta la obtención de los resultados en la computadora.
CONTENIDO
TDA Entero: tiene como tipo el conjunto de números enteros, y como operaciones la suma, resta,
multiplicación y división entera
TDA Real: tiene como tipo el conjunto de números reales y como operaciones la suma, resta
multiplicación y división
TDA Booleano: tiene como tipo el conjunto de valores booleanos True, False y como operaciones las
definidas en el álgebra de Boole (AND, OR, NOT)
TDA Carácter: tiene como tipo el conjunto de caracteres definido por un alfabeto dado y como
operaciones todos los operadores relacionales (<, >, =, >=, <=, <>)
Se denominan tipos de datos escalares a aquellos en los que el conjunto de valores está ordenado y
cada valor es atómico: Carácter, Entero, Real y Booleano.
Se denominan tipos de datos ordinales a aquellos en los que cada valor tiene un predecesor (excepto el
primero), y un único sucesor (excepto el último): Carácter, Entero y Booleano.
Los lenguajes de alto nivel suelen presentar tres funciones adicionales asociadas a los tipos de datos
ordinales: posición (Ord), predecesor (Pred) y sucesor (Suc) de un elemento.
6
Otro tipo de datos son los denominados compuestos, dado que son divisibles en componentes que
pueden ser accedidas individualmente. Las operaciones asociadas a los tipos compuestos o
estructurados son las de almacenar y recuperar sus componentes individuales.
TDA Conjunto: colección de elementos tratados con las operaciones unión, intersección y diferencia
de conjuntos.
TDA Arreglo: colección homogénea de longitud fija tal que cada una de sus componentes pueden ser
accedidas individualmente mediante uno o varios índices, que serán de tipo ordinal, y que indican la
posición de la componente dentro de la colección.
TDA Registro: tipo de datos heterogéneo compuesto por un número fijo de componentes denominadas
campos a las que se accede mediante un selector de campo.
El TDA Conjunto no es estructurado ya que no está organizado mediante el modo de acceso a sus
componentes individuales, mientras que el Arreglo y el Registro si lo son. Los registros pueden tener
como campos tipos de datos simples o arreglos o incluso otros registros.
Los TDA básicos son los utilizados con mayor frecuencia, pero además cada problema puede requerir
la definición de varios TDA propios. Por ejemplo, un TDA muy utilizado en aplicaciones informáticas
es el TDA lista definido de la siguiente forma:
TDA lista (o Secuencia): colección homogénea de datos, ordenados según su posición en ella, tal que
cada elemento tiene un predecesor (excepto el primero) y un sucesor (excepto el último) y los
operadores asociados son:
• Insertar: inserta un elemento x, en una posición p de la lista
• Localizar: localiza la posición p en la que se encuentra el dato x
• Recuperar: encuentra el elemento x que esta en la posición p
• Suprimir: elimina de la lista el elemento de la posición p
• Suprimir_dato: elimina de la lista cualquier aparición del elemento x
• Anula: ocasiona que la lista se vacíe
• Primero (Fin): proporciona el primer (último) elemento de la lista
• Imprimir: imprime todos los elementos de la lista en su orden.
En general el término “lista” se utiliza cuando el TDA se implementa sobre memoria principal mientras
que “secuencia” suele reservarse para implementaciones sobre memoria secundaria.
La lista es una estructura dinámica ya que su longitud dependerá del número de elementos que tenga,
aumentará al insertar y se reducirá al suprimir. Ahora bien, la lista puede implementarse de formas muy
diferentes. Una de ellas es mediante arreglos. Así, la lista, que es dinámica independientemente de su
implementación, se realiza mediante una implementación estática.
Estructura
Es un conjunto de elementos entrelazados regidos por una serie de leyes. En general, las estructuras que
nos presenta el mundo real están regidas por las leyes físicas.
Estructura de Datos
Una estructura de datos es una colección organizada de elementos de datos bajo las leyes de la
informática. Estos elementos, o ladrillos, son los tipos de datos, con los cuales se forman las estructuras
de datos que incluyen los diferentes lenguajes:
7
• Binarios
• Enteros
• Reales
• Caracteres
o Doble precisión
o Vectores
o Arreglos bidimensionales
o Arreglos n-dimensionales
o Registros
• Complejos
Debido a lo limitado de los lenguajes de alto nivel, los programadores estaban insatisfechos por el nivel
de abstracción que lograban.
Este problema, se ha superado al proveer los nuevos lenguajes con mecanismos que le permiten al
programador construir sus propias abstracciones con base en su necesidad, a través de los tipos de datos
definidos por el usuario.
El enfoque de los nuevos lenguajes, orientados por objetos, es tener un conjunto fijo de estructuras y un
conjunto poderoso de primitivas que permiten su manipulación, brindandole al usuario la oportunidad
de definir datos (atributos) y operaciones (métodos).
Estructura Lógica. Es la estructura que define el usuario a nivel de análisis y parte de diseño, al
relacionar lógicamente los elementos para satisfacer un requerimiento determinado.
struct ID_CARRO {
char placa[6];
struct PROPIETARIO prop;
struct CARACTER car;
8
};
struct PROPIETARIO {
char direccion[50];
struct NOMBRE nomb;
};
struct CARACTERISTICAS {
char marca[20];
int modelo[2];
char color[10];
};
struct NOMBRE {
char nombre[10];
char apellido_1[10];
char apellido_2[10];
};
Función Constructora Son los algoritmos y mecanismos que permiten construir una estructura.
Asigna memoria en forma dinámica o espacios en medios magnéticos.
Función Destructora Son los algoritmos y mecanismos que desasignan memoria y liberan recursos
del sistema.
Función de Acceso Son los algoritmos a través de los cuales se llega a una estructura y sus elementos
Función de Prueba Por medio de la función de acceso se llega a un elemento y se prueba si posee
determinado atributo.
Función Inversa Las funciones de acceso proveen un nombre o valor. Estas funciones son los
mecanismos o procesos, en que dado un valor o un nombre, se determina la posición del elemento de
dato en la estructura.
9
TEMA 2
PILAS Y COLAS
OBJETIVOS
Emplear y desarrollar aplicaciones empleando estructuras estáticas de datos como apoyo a procesos
más complejos.
CONTENIDO
2.1 Pilas
2.2 Colas
2.3 Colas Circulares
2.4 Aplicaciones de Listas Restringidas
2.5 Ejercicios
En este capítulo se introducen dos tipos de listas, pilas y colas, que se pueden actualizar, solo de una
manera muy limitada. Son estructuras bastante importantes utilizadas en multitud de aplicaciones.
2.1 PILAS.
Una pila es una lista en donde tanto las inserciones como las supresiones se hacen por el mismo
extremo, que se conoce como el tope de la pila.
Por ejemplo, la pila de carritos de los supermercados, porque el último carro que se coloca es el
primero que se saca. Esta propiedad de la pila se conoce como LIFO (Last In First Out; último que
entra primero que sale).
PILA SECUENCIAL
Aquí se trabaja como una lista secuencial de acceso restringido. Para su manejo se requieren dos
apuntadores, uno que indica la posición del último elemento insertado, llamado TOPE de la pila, y otro
denominado BASE que apunta al fondo de la pila. Cuando la pila está vacía, no hay elementos,
BASE=TOPE.
Figura 2.1 Pila, se inserta y se elimina por el mismo extremo. El apuntador TOPE apunta al último
elemento de la pila; el apuntador BASE no apunta a ningún elemento de la pila
10
OPERACIONES CON PILAS
Para la implementación de esta estructura de datos, se hará uso de un arreglo que aquí llamamos PILA,
de dimensión M. Esta es la estructura estática con la que se trabajan estos algoritmos.
INIC_PILA
Inicio
BASE = 0
TOPE = BASE
Fin
Aquí, la base apunta al elemento 0 del arreglo, que como se había determinado, no corresponde a
ningún elemento de la lista.
Prueba de Pila Vacía: En general es importante, para todo tipo de estructura, establecer si se
encuentra vacía, pues el proceso varía dependiendo de esta condición.
Precondición: No hay.
Poscondición: Retorna verdadero cuando no hay elementos en la pila. En caso
contrario retorna el valor falso.
ENTERO PILA_VACIA()
Inicio
Si (TOPE==BASE) Entonces
RETORNA VERDADERO
Sino
RETORNA FALSO
Fin-Si
Fin
Esta rutina se emplea en el algoritmo ELIM_PILA que saca el elemento que apunta el TOPE de la pila.
11
Acceder al Elemento Tope de la Pila: Con esta operación se lee el último elemento de la pila y se
lleva a DATO.
ACCE_PILA(DATO,I)
Inicio
I = Verdadero
Si (NO PILA_VACIA()) Entonces
DATO = PILA[TOPE]
Sino
I = Falso
Fin-Si
Fin
Insertar un Dato en la Pila: Se coloca DATO en un nodo que queda en el TOPE de la pila. Es posible
que no se pueda hacer la inserción debido a que no hay nodos disponibles recordemos que con la
variable I se indica si se pudo, o no, hacer la inserción.
INSER_PILA(DATO,I)
Inicio
I = Verdadero
Si (TOPE>=M) Entonces
I = Falso
Sino
TOPE = TOPE + 1
PILA[TOPE] = DATO
Fin-Si
Fin
La condición de saturación generalmente es un error y significa que el área disponible está llena y que
aun se tratan de colocar más datos en ella. La política común es, en primer lugar, tratar de manejar
dicha condición para superarla, o rechazar la inserción e informar al sistema operacional para una
terminación anormal de la tarea.
12
Eliminar un Elemento de la Pila: En DATO se coloca el elemento que inicialmente apunta al TOPE
de la pila. Al disminuir TOPE el elemento anterior permanece en el área de memoria como basura.
ALGORITMO ELIM_PILA(DATO,I)
Inicio
I = Verdadero
Si (NO PILA_VACIA()) Entonces
DATO = PILA[TOPE]
TOPE = TOPE - 1
Sino
I = Falso
Fin-Si
Fin
La condición de pila vacía se presenta cuando se trata de obtener un elemento inexistente, la cual no es
una condición de error.
Esta condición se puede considerar en el sentido de que hay una serie de recursos disponibles para
procesar el DATO que se espera, en caso de que no llegue, simplemente se liberan los recursos o se
inicia el proceso de los datos anteriores.
Es semejante a la condición que se presenta cuando se está procesando un archivo secuencial y se llega
al fin del archivo.
ALGORITMO DEST_PILA
Inicio
I = Verdadero
Mientras I Hacer
ELIM_PILA(DATO,I)
Rutina_usa (Dato)
Fin-M
Fin
En muchas aplicaciones es necesario vaciar la pila, para esto se incluye la rutina apropiada que utiliza
DATO dentro del ciclo.
13
DISTRIBUCIÓN DE PILAS.
En los lenguajes de programación, las subrutinas y los diferentes componentes de los sistemas
operacionales requieren la utilización de varias pilas, las cuales se pueden disponer de muchas formas,
buscando optimizar el uso de la memoria.
Como se vio en la inserción, el hecho de que haya saturación implica una terminación anormal,
perdiéndose todo lo procesado.
Figura 2.3 Pilas enfrentadas. El área común es todo el arreglo con sus M elementos. La base de la
pila 1 tiene el índice 0 y el índice de la base de la pila 2 es M.
Se debe observar la figura 2.3 y dejar claro cómo se trabajan los índices y a que nodos apuntan. En los
siguientes algoritmos se incluye la variable IP (indicador de pila) que dirá sobre cual de las dos pilas se
debe realizar la operación.
INIC2PILAS
Inicio
BASE1 = 0; TOPE1 = BASE1
BASE2 = M; TOPE2 = BASE2
Fin
ALGORITMO 2PILASVAC(IP,VACIA)
Inicio
Si (IP=1) Entonces
Si (BASE1 = TOPE1)
VACIA = V
Sino
VACIA = F
14
Fin-Si
Sino
Si (BASE2 = TOPE2)
VACIA = V
Sino
VACIA = F
Fin-Si
Fin-Si
Fin
ACC2PILAS(DATO,IP,I)
Inicio
I = V
2PILASVAC(IP,VACIA)
Si (VACIA) Entonces
I = F
Sino
Si (IP=1) Entonces
DATO = PILA[TOPE1]
Sino
DATO = PILA[TOPE2]
Fin-Si
Fin-Si
Fin
ALGORITMO INSER2PILAS(DATO,IP,I)
Incio
I = F
Si ((TOPE1+1) < TOPE2) Entonces
Si (IP=1) Entonces
TOPE1 = TOPE1 + 1
PILA[TOPE1] = DATO
Sino
TOPE2 = TOPE2 - 1
PILA[TOPE2] = DATO
Fin-Si
Sino
I = V
Fin-Si
Fin
15
Eliminar un Elemento de una de las Pilas
ALGORITMO ELIM2PILAS(DATO,IP,I)
Inicio
I = V
2PILASVAC(IP,VACIA)
Si (NO VACIA) Entonces
Si (IP = 1) Entonces
DATO = PILA[TOPE1]
TOPE1 = TOPE1 - 1
Sino
DATO = PILA[TOPE2]
TOPE2 = TOPE2 + 1
Fin-Si
Sino
I = F
Fin-Si
Fin
Precondición:
Poscondición: Se elimina el elemento tope de pila IP.
ALGORITMO DESTR2PILAS(IP)
Incio
Si (IP = 1) Entonces
Mientras I Ejecute
ELIM_2PILAS(DATO,IP,I)
Rutina_Usa(Dato)
Fin-M
Sino
Mientras I Ejecute
ELIM_2PILAS(DATO,IP,I)
Rutina_Usa(Dato)
Fin-M
Fin-Si
Fin
16
2.2 COLAS.
Es una lista de proceso restringido en la cual las inserciones se hacen por un extremo llamado "último"
y las eliminaciones se hacen por el otro extremo llamado "delantero". Por ejemplo, los aviones que
esperan despegar de un aeropuerto forman una cola, el que llega a la pista queda de último y el que va a
despegar está de delantero en la cola. Se dice que una cola tiene política FIFO(First In, First Out) El
primer elemento en entrar será el primero en salir.
COLA SECUENCIAL
Aquí se trabaja la cola como una lista secuencial de acceso restringido, dentro del arreglo COLA. Para
su manejo se requieren dos índices, ULTIMO que indica el último elemento insertado en la cola, y
DELANTERO que indica el primer elemento de la cola.
Figura 2.4 Cola, se inserta por el último y se suprime por delante. El apuntador U, último, apunta al
último elemento insertado. El apuntador D, delantero, apunta al primer elemento en la cola
Sobre una cola se pueden efectuar las siguientes operaciones primitivas, similares a las operaciones de
la pila:
INICIAR COLA.
Se tiene un arreglo COLA asignado para almacenar la cola, con M posiciones disponibles.
INIC-COLA
Inicio
D = 0
U = D
Fin
17
PROBAR SI LA COLA ESTÁ VACÍA
Se coloca DATO en el nodo siguiente al último, quedando como último. Es posible que no se pueda
hacer la inserción debido a que no hay espacio. La variable I indica si se pudo o no hacer la inserción.
18
ELIMINAR UN ELEMENTO DE LA COLA.
DESTRUIR LA COLA
19
Figura 2.5 Después de que U = M se U = 1.
Los algoritmos de iniciación, prueba, acceso y destrucción de la cola son similares a los del caso
anterior. Los de inserción y eliminación varía por la condición inicial: U=0 U=D; y por la condición de
saturación: D=U.
Aunque es similar al de inserción en colas comunes, se debe considerar, cuando U = M, para enviarlo a
0. También se debe tener en cuenta la nueva condición de saturación.
20
Figura 2.7 (a) Condición inicial crítica
(b) Condición final una vez se ha eliminado el nodo delantero
21
2.4 APLICACIONES DE LISTAS RESTRINGIDAS.
Son muchas las aplicaciones de estas listas restringidas, en especial en sistemas operativos,
compiladores e interpretadores. Vamos a trabajar una aplicación universal, la evaluación de una
expresión.
La gran mayoría de lenguajes representa las expresiones aritméticas en la llamada forma INFIJA, en la
cual los operadores se encuentran dentro de los operandos. Otra manera de hacerlo es usando notación
POSFIJA o POLACA. En este tipo de notación los operadores aparecen después de los operandos, así
se facilita la evaluación de la expresión.
Vamos a suponer que se tienen los siguientes operadores, con sus respectivas prioridades.
El algoritmo analiza carácter a carácter la expresión infija, de izquierda a derecha y sigue el siguiente
manejo:
Si el operador que entra, tiene mayor prioridad que el operador que está en el tope de la pila, se
coloca en el tope de la pila.
Si el operador que entra es de prioridad menor o igual al del tope de la pila, la pila se desocupa
enviando los operadores a la cola, hasta que el operador tenga mayor prioridad al del tope de la
pila.
22
2. Los operandos consisten de una sola letra
3. No se espera la entrada de constantes ni de referencia a funciones
4. No deben aparecer funciones unarias.
P( ) ) = -1 P(-) = 1
P(( ) = -1 P(*) = 2
P( = ) = 0 P(/) = 2
P(+) = 1 P(^) = 3
ALGORITMO INF-A-POSF
Precondición: Únicamente los caracteres ya definidos.
Poscondición:
Comience
CREA_PILA
D = 0
TOPE = 0
INSER-PILA( ,I)
Si (I) Entonces
J = 1
K = 1
Mientras (K < N) Ejecute
Si (X[K] = LETRA) Entonces
Y[J] = X[K]
J = J+1
Sino
Si (X[K] = ")" ) Entonces
Mientras (PILA [TOPE]) <> "(" ) Ejecute
Y[J] = PILA[TOPE]
J = J+1
TOPE = TOPE - 1
Fin-Mientras
TOPE = TOPE - 1
Sino
Mientras (P(X[K])<= P(PILA[TOPE]) Ejecute
Y[J] = PILA[TOPE]
J = J+1
TOPE = TOPE-1
Fin-Mientras
TOPE=TOPE+1
PILA[TOPE] = X[K]
Fin-Si
Fin-Si
K=K+1
Fin-Mientras
Mientras [(PILA[TOPE]) <>(I)] Ejecute
Y[J] = PILA[TOPE]
J = J+1
TOPE = TOPE -1
Fin-Mientras
23
Sino
Fin-Si
Termine
En este caso se tiene una expresión aritmética en notación posfija se encuentra en un arreglo X. Cada
X(J), J=1,...N., puede ser una letra o un operador (<= , +, -, *, /, ).
Los operandos se van guardando en una pila hasta cuando se encuentra un operador, que hace que se
ejecute esa operación entre los dos nodos operandos que se tengan en los nodos superiores de la pila.
ALGORITMO EVAL-POL
Precondición:
Poscondición:
Comience
TOPE = 0
K = 1
Mientras (K<=N) Ejecute
Si (X[K] = OPERANDO) Entonces
TOPE = TOPE +1
PILA[TOPE] = X[K]
Sino
AUX1 = PILA[TOPE]
TOPE = TOPE-1
AUX2 = PILA[TOPE]
AUX3 = AUX2 (X[K]) AUX1
PILA[TOPE] = AUX3
Fin-Si
K = K +1
Fin-Mientras
Termine
Vamos a seguir los dos algoritmos anteriores para la evaluación de la siguiente expresión: G = ((A-
B)*C)+(D/(E ^F))
1. Conversión a Posfija.
X COLA PILA
. .
1 G
2 G =
3 G =(
24
4 G =((
5 GA =((
6 GA =((-
7 GAB =((-
8 GAB- =(
9 GAB- =(*
10 GAB-C =(*
11 GAB-C* =
12 GAB-C* =+
13 GAB-C* =+(
14 GAB-C*D =+(
15 GAB-C*D =+(/
16 GAB-C*D =+(/(
17 GAB-C*DE =+(/(
18 GAB-C*DE =+(/(^
19 GAB-C*DEF =+(/(^
20 GAB-C*DEF^ =+(/
21 GAB-C*DEF^/ =+
22 GAB-C*DEF^/+
23 GAB-C*DEF^/+=
2. Evaluación de la notación Posfija, donde A=4, B=1, C=5, D=64 , E=2, F=5
X COLA PILA
1 G G
2 GA G4
3 GAB G41
4 G3 G3
5 G3C G35
6 G 15 G 15
7 G 15 D G 15 64
8 G 15 D E G 15 64 2
9 G 15 D E F G 15 64 2 5
10 G 15 D 32 G 15 64 32
11 G 15 2 G 15 2
12 G 17 G 17
13 17 17
25
TEMA 3
CONTENIDO
3.1 Archivos
3.2 Registros y Campos
3.3 Archivos Secuenciales y Directos
3.1 ARCHIVOS
Un archivo es un conjunto de registros relacionados entre sí, que forman una entidad, que se puede
identificar y manipular de modo unitario.
Un archivo almacena grandes volúmenes de información en unidades externas (Floppy, HD, Cintas ..).
Son aquellos en los que los registros se graban unos a continuación de otros y el acceso a un registros
determinado, necesita forzosamente el paso por todas los registros anteriores.
Se realiza este tipo de definición de campos de longitud variable, con el objetivo principal de
ahorro de memoria, ya que con campos de longitud fija se realiza un desperdicio de memoria.
Para la implementación de este tipo de campos se lo realiza con archivos de tipo texto.
Existen dos mecanismos de implementación:
USO DE DELIMITACIONES
DELIMITADOR DE CAMPOS :
DELIMITADOR DE REGISTROS :
Ejemplo :
26
Nombre Dirección Edad Lista de Materias
SUBCAMPO DE LONGITUD
Ejemplo :
Para distinguir algunos registros que contienen los mismos campos se recurre a un campo que contiene
información única o diferente que se le llama clave.
ARCHIVOS DIRECTOS.
Son aquellos en los que los registros se pueden localizar mediante una dirección. El acceso a los
registros es directo.
Los campos definidos de esta manera son almacenados en memoria con una longitud fija de datos,
sin importar si se realiza el uso parcial o completo del campo.
Creación. Consiste en la grabación, por primera vez, en un soporte de los registros de un determinado
archivo.
Apertura y Cierre. Un archivo debe poseer las condiciones necesarias para ser abierto, manipularse y
tener acceso a los registros. Para evitar deterioros de información el archivo es cerrado durante el
tiempo que no se utiliza y no debe permitir su acceso.
Copiado o Duplicación. Consiste en crear un archivo idéntico a otro y existencte, sin que se afecte el
original.
27
Concatenación. Consiste en obtener de dos archivos, con la misma estructura, un archivo que contenga
ambos, de manera que queden uno a continuación del otro.
Intersección. Consiste en formar un único archivo mediante los registros comunes de dos archivos
distintos.
Función o Intercalación. Llamada mezcla, consiste en obtener de dos archivos ordenados por un
mismo campo y con la misma estructura, otro archivo que contenga los registros de ambos y mantenga
su ordenación.
Partición. Consiste en dividir un archivo en dos, de acuerdo a alguna característica determinada de sus
registros.
Un registro es un conjunto organizado de datos del mismo o diferente tipo, que estan relacionados entre
sí por contener información relativa a una entidad particular y superior, de la que forman parte y los
unifica.
Los registros están compuestos por campos. Un campo es una unidad mínima de información, dentro
del registro, independiente de las demás. Representa un concepto global en la finalidad unitaria del
registro y se caracteriza por el tipo de datos asociados y el tamaño de memoria que necesita para su
almacenamiento. El campo se puede dividir en subcampos.
CAMPOS.
Los componentes de un registro se denominan campos, los campos son los atributos de los
objetos de estudio, cada campo tiene los siguientes atributos.
TIPOS DE CAMPOS.
• Longitud fija.
• Longitud variable
• Campos múltiples
28
TIPOS DE REGISTROS
Registro Lógico. Cada uno de los componentes de un archivo que posee una estructura para almacenar
información referente a un tema general del archivo. Diseñado por el programador.
Registro Físico. Información que el sistema puede transferir como una unidad, en una sola operación de
entrada o salida. Registros según la longitud de sus campos:
Registros de Longitud Fija. Su tamaño no varía. En cuanto a su estructura interna, el registro puede
tener el mismo número de campos de igual longitud cada uno, o diverso número de campos de
diferentes longitudes. En ambos casos la suma de las longitudes de los campos es la misma para cada
registro.
Registros de Longitud Variable. Su tamaño varía de un registro a otro. Su longitud variable puede estar
determinada por un valor máximo o también sin ninguna limitación. Entre las agrupaciones
estructuradas de registros se tienen los archivos de datos y las bases de datos.
Aquí se analizan las operaciones que solo afectan a algunos registros. Todas estas operaciones
necesitan la operación de búsqueda como preliminar para localizar los registros deseados.
Recuperación (Consultas y Listados). Consiste en acceder a los registros de un archivo para obtener
una información determinada, almacenada en el.
29
TEMA 4
TRATAMIENTO DE ARCHIVOS
OBJETIVOS
CONTENIDO
4.1 Introducción
4.2 Operaciones en Archivos
4.3 Partición de Archivos
4.4 Mezclas de Archivos
4.5 Métodos Ordenación Externa
4.1 INTRODUCCION
Un archivo es una colección de registros, donde cada registro esta conformado por un conjunto de
campos.
El objetivo principal del siguiente tema es realizar las siguientes operaciones para el tratamiento de
archivos:
1. Almacenamiento de información.
2. Recuperación de información.
3. Actualización de la información.
1. Adición de datos
2. Eliminación de datos
3. Consultas
4. Búsquedas
1. Partición de Archivos
2. Mezcla o fusión de Archivos
30
Estas últimas son operaciones auxiliares, necesarias para los métodos de ordenación externa.
ADICION DE DATOS
Una de las tareas mas comunes en archivos es la adición de datos, se puede añadir datos de dos
maneras a un archivo:
Si no :
Añadir el dato al final del Archivo
Para añadir un registro manteniendo el orden del archivo, es necesario el uso de un archivo auxiliar.
31
ELIMINAR DATOS
1. Eliminar lógicamente
2. Eliminara físicamente
FÍSICAMENTE:
En este tipo de eliminación es necesario usar un archivo Auxiliar, por lo que este proceso es lento, y
por lo tanto no es usado con frecuencia.
LOGICAMENTE:
Este tipo de eliminación ahorra tiempo cuando se usa grandes volúmenes de información. Ya que se
cuenta con un campo adicional, que permite marcar el registro como eliminado.
32
Muchas aplicaciones usan primeramente la eliminación lógica, y al finalizar el día utilizan la
eliminación de física. Esto por ser más rápida la eliminación lógica que la eliminación física.
CONSULTAS DE DATOS
Se recupera la información sin alterar el estado del registro, para lo cual debe existir un criterio de
selección de la información.
CRITERIOS DE SELECCIÓN
Consulta Simple:
Consulta funcional:
33
Fin mientras
Consulta lógicas:
BUSQUEDA DE DATOS
Se realiza el recorrido de los registros de archivo hasta el incumplimiento de una condición o que
finalice el archivo.
Inicio
Leer Clave
Abrir (F,Lectura)
Leer (F,reg)
Mientras no eof (F) y reg <= clave hacer
Leer (F,reg)
Fin mientras
Si reg=clave Entonces
Mostrar " Existe el dato"
Si no
Mostrar "No existe el dato"
Fin si
Cerrar (F)
Fin
34
BÚSQUEDA SECUENCIAL EN UN ARCHIVO DESORDENADO
Se realiza el recorrido de los registros hasta encontrar el registro o hasta que se finalice el archivo.
ALGORITMO
Inicio
Leer Dato
Abrir (F,Lectura)
Leer (F,reg)
Mientras no eof (F) y reg <> clave hacer
Leer (F,reg)
Fin mientras
Si reg=clave Entonces
Mostrar " Existe el dato"
Si no
Mostrar "No existe el dato"
Fin si
Cerrar (F)
Fin
35
PARTICION POR CONTENIDO
Se debe establecer un criterio de partición, para determinar que registros de F1 se destinan al archivo
F2.
Ejemplo: Crear un archivo llamado CARRERA seleccionando Clave = 'SIS' del Archivo FNI.
ALGORITMO
Inicio
Leer Clave
Abrir FNI (Lectura)
Abrir Carrera (Escritura)
Mientras no eof (FNI) Hacer
Leer (FNI, Reg)
Si Reg = Clave Entonces
Grabar (Carrera, Reg)
Fin si
Fin Mientras
Cerrar (FNI)
Cerrar (Carrera)
Fin
Se establece un numero entero P que índica las secuencias para realizar la partición. Se copia 'P'
registros de Arch a Arch1, luego los siguientes 'P' registros de Arch a Arch2, esto se repite hasta
terminar de leer el archivo Arch
Ejemplo:
Realizar la partición del archivo ARCH en dos archivos ARCH1 y ARCH2, utilizando la secuencia de
partición (P = 2).
36
ALGORITMO
Inicio
Leer (P)
Abrir Arch(Lectura)
Abrir Arch1(Escritura)
Abrir Arch2(Escritura)
Mientras no eof (Arch) Hacer
Copiar_Secuencia(Arch, Arch1, P)
Si no eof (Arch) Entonces
Copiar_Secuencia(Arch, Arch2, P)
Fin si
Fin Mientras
Cerrar (Arch)
Cerrar (Arch1)
Cerrar (Arch2)
Fin
Se toma primeramente un tramo ordenado del archivo C y se copia al archivo A, luego se toma otro
tramo ordenado del archivo C para copiarlo al archivo B este proceso se repite hasta que finalice de leer
todo el archivo C.
ALGORITMO
Inicio
Abrir A (Lectura)
Abrir B (Lectura)
Abrir C (Escritura)
Mientras no eof (C) Hacer
Copiar_Tramo (C, A )
Si no eof (C) Entonces
Copiar_Tramo (C, B )
37
Fin si
Fin Mientras
Cerrar (A)
Cerrar (B)
Cerrar (C)
Fin
38
MEZCLA POR CONTENIDO TOTAL
Se procede a la unión de dos archivos A, B en un archivo C, este proceso se los realiza primeramente
copiando toda la información de A al C y posteriormente B al C.
Ejemplo:
Mezclar los Archivos A, B en un tercer archivo C.
ALGORITMO
Inicio
Abrir A (Lectura)
Abrir B (Lectura)
Abrir C (Escritura)
Mientras no eof (A) Hacer
Leer (A, Reg)
Grabar (C, Reg)
Fin Mientras
Mientras no eof (B) Hacer
Leer (B, Reg)
Grabar (C, Reg)
Fin Mientras
Cerrar (A)
Cerrar (B)
Cerrar (C)
Fin
Se establece un numero entero 'P', que indica las secuencias para realizar la mezcla de dos archivos. El
resultado de la mezcla es una secuencia ordenada de datos.
39
ALGORITMO
Inicio
Leer (P)
Abrir A (Lectura)
Abrir B (Lectura)
Abrir C (Escritura)
Cerrar (A)
Cerrar (B)
Cerrar (C)
Fin
Mezclar_SecP
Inicio
SecA =0; SecB=0
Repetir
PosA = FilePos(A);Leer(A, RegA);Seek (A, PosA)
PosB = FilePos(B);Leer(B, RegB);Seek (B, PosB)
40
MEZCLA POR TRAMOS ORDENADAS
Se realiza la mezcla los archivos A y B en el archivo C, tomando tramos ordenados. Es decir se realiza
la mezcla del primer tramo ordenado de A con la de B.
ALGORITMO
Inicio
Abrir A (Lectura)
Abrir B (Lectura)
Abrir C (Escritura)
Mientras no eof (A) y no eof (B) Hacer
Mezclar_Tramo
Fin Mientras
Mientras no eof (A) Hacer
Copiar_Tramo (A, C)
Fin Mientras
Mientras no eof (B) Hacer
Copiar_Tramo (B, C)
Fin Mientras
Cerrar (A)
Cerrar (B)
Cerrar (C)
Fin
Mezclar_Tramo
Inicio
Repetir
PosA = FilePos (A)
Leer (A, RegA)
Seek (A, PosA)
41
Si no
Copiar_Dato (B, C)
Si Fin_Tramo Entonces
Copiar_Tramo (A, C)
Fin si
Fin si
Hasta Fin _Tramo
Fin
Todos estos métodos hacen el uso de archivos auxiliares, para realizar el proceso de ordenación de
archivos.
El método solo funciona con archivos donde su longitud sea múltiplo de 2n, hace el uso de los
siguientes archivos:
42
C Archivo a ordenar
A,B Archivos auxiliares
1. Partir el archivo C en la mitad, copiar la primera mitad del archivo C en el archivo A, y la otra
mitad en el archivo B.
2. Mezclar los archivos auxiliares A y B por secuencias de 2n. Donde: n = 0,1,2,3...
3. Repetir los pasos 1 y 2 hasta que la secuencia sea mayor que la mitad del archivo a ordenar
(secuencia{2n}> long(C)/2)
Ejemplo:
ALGORITMO
Inicio
Sec = 1
Abrir C (Lectura)
Repetir
Abrir B (Escritura)
Abrir A (Escritura)
Seek (C, 0)
Particionar
Seek (A, 0); Seek (B, 0); Seek (C, 0)
Mezclar
43
Cerrar A; Cerrar B
Sec = Sec * 2
Hasta Sec > (Filesize (C) Div 2)
Cerrar C
Fin
Particionar
Inicio
Max = Filesize (C) Div 2
Para J = 1 Hasta Max Hacer
Leer C, Reg
Grabar A, Reg
Fin Para
Para J = 1 Hasta Max Hacer
Leer C, Reg
Grabar B, Reg
Fin Para
Fin
Mezclar
Inicio
Mientras no eof (A) y no eof (B) Hacer
Mezclar_Sec
Fin Mientras
Fin
Mezclar_Sec
Inicio
SecA = 0; SecB = 0
Repetir
PosA = FilePos (A); Leer A, RegA; Seek (A, PosA)
PosB = FilePos (B); Leer B, RegB; Seek (B, PosB)
Si RegA < RegB Entonces
Leer A, Reg
Grabar C, Reg
SecA =SecA + 1
Si SecA = Sec Entonces
Copiar_SecP (B, C, Sec-SecB);
SecB = Sec
Fin si
Si no
Leer B, Reg
Grabar C, Reg
SecB = SecB + 1
Si SecB = Sec Entonces
Copiar_SecP (A, C, Sec-SecA);
SecA = Sec
Fin si
Fin si
Hasta (SecA = Sec) y (SecB = Sec)
Fin
44
Fin Mientras
Fin
El método solo funciona con archivos donde su longitud sea múltiplo de 2n, hace el uso de los
siguientes archivos:
C Archivo a ordenar
A,B Archivos auxiliares
1. Partir el archivo C en secuencias de 2n, copiar intercaladamente las secuencias del archivo C,
primeramente en el archivo A, y luego en el archivo B.
2. Mezclar los archivos auxiliares A y B por secuencias de 2n. Donde: n = 0,1,2,3...
3. Repetir los pasos 1 y 2 hasta que la secuencia sea mayor que la mitad del archivo a ordenar
(secuencia{2n}> long(C)/2)
Ejemplo:
45
ALGORITMO
Inicio
Sec = 1
Abrir C (Lectura)
Repetir
Abrir B (Escritura)
Abrir A (Escritura)
Seek (C, 0)
Particionar
Seek (A, 0); Seek (B, 0); Seek (C, 0)
Mezclar
Cerrar A; Cerrar B
Sec = Sec * 2
Hasta Sec > (Filesize (C) Div 2)
Cerrar C
Fin
Particionar
Inicio
Mientras no eof (C) Hacer
Copiar_SecP (C, A, Sec)
Si no eof (C) Entonces
Copiar_SecP(C,B,Sec)
Fin si
Fin mientras
Fin
Mezclar
Inicio
Mientras no eof (A) y no eof (B) Hacer
Mezclar_Sec
Fin Mientras
Fin
Mezclar_Sec
Inicio
SecA = 0; SecB = 0
Repetir
PosA = FilePos (A)
Leer A, RegA
Seek (A, PosA)
46
Si RegA < RegB Entonces
Leer A, Reg
Grabar C, Reg
SecA =SecA + 1
Si SecA = Sec Entonces
Copiar_SecP (B, C, Sec-SecB);
SecB = Sec
Fin si
Si no
SecB = SecB + 1
Si SecB = Sec Entonces
Copiar_SecP (A, C, Sec-SecA);
SecA = Sec
Fin si
Fin si
Hasta (SecA = Sec) y (SecB = Sec)
Fin
El método funciona con todo tipo de archivos, hace el uso de los siguientes archivos:
C Archivo a ordenar
A,B Archivos auxiliares
47
ALGORITMO
PRINCIPAL
Inicio
Abrir C (Lectura)
Repetir
Abrir A (Escritura);Abrir B (Escritura);
Seek (C, 0)
Particionar
Seek (C, 0); Seek (A, 0); Seek (B, 0)
Num_Tramos = 0
Mezclar;
Cerrar A; Cerrar B
Hasta Num_Tramos =1
Cerrar C
Fin
Particionar
Inicio
Mientras no eof (C) Hacer
Copiar Tramo (C, A)
Si no eof (C) Entonces
Copiar_Tramo(C,B)
Fin si
Fin mientras
Fin
Mezclar
Inicio
48
Mientras no ef (A) Hacer
Copiar_Tramo (A, C)
Num_Tramos = Num_Tramos + 1
Fin Mientras
Fin
Mezclar_Tramo
Inicio
Repetir
PosA = File Pos (A);
Leer (A, RegA);
Seek (A, PosA)
PosB = File Pos (B);
Leer (B, RegB);
Seek (B, PosB)
Si RegA < RegB Entonces
Copiar_Dato (A, C)
Si Fin_Tramo Entonces
Copiar_Tramo (B, C)
Fin Si
Si no
Copiar_Dato (B, C)
Si Fin_Tramo Entonces
Copiar_Tramo (A, C)
Fin si
Fin si
Hasta Fin _Tramo
Fin
El método funciona solo con archivos que almacenan datos numéricos con cifras elevadas, hace el uso
de los siguientes archivos:
C Archivo a ordenar
A0, A1..., A9 Archivos auxiliares
49
Ejemplo:Ordenar los siguientes datos.
ALGORITMO
FUNCION PRINCIPAL
Var Arch : [0....9] of Archivo
Inicio
Abrir C (Lectura)
Dig = 0
Repetir
Abrir_Archivos
Seek (C, 0)
Particionar
Para I = 0 Hasta 9 Hacer Seek (Arch [ i ], 0)
Seek (C, 0)
Mezclar
Dig = Dig + 1
Cerrar_Archivos
Hasta Dig > 4
Cerrar C
Fin
Particionar
Inicio
Mientras no eof (C) Hacer
50
Leer C, Reg
Aux = Reg Div (Exp(Dig * Ln (10)))
Pos = Aux Mod 10
Grabar Arch [Pos], Reg
Fin Mientras
Fin
Mezclar
Inicio
Para I = 0 Hasta 9 Hacer
Mientras no eof (Arch [I]) Hacer
Leer Arch [I], Reg
Grabar C, Reg
Fin Mientras
Fin Para
Fin
Abrir_Archivos
Inicio
Para I = 0 Hasta 9 Hacer
NOMBRE = 'DATOS'+ CHR(48 + i)+ '.DAT'
Abrir Arch [i] (Escritura)
Fin Para
Fin
Cerrar_Archivos
Inicio
Para I = 0 Hasta 9 Hacer
Cerrar Arch [ i ]
fin Para
Fin
51
TEMA 5
LISTAS ENCADENADAS
OBJETIVOS
Desarrollar algoritmos para optimizar las operaciones básicas que se llevan a cabo en proceso de
ejecución en estructuras lineales.
CONTENIDO
5.1 Introducción
5.2 Lista Simplemente Enlazada
5.3 Listas Simplemente Enlazadas Circulares
5.4 Listas Doblemente Enlazadas
5.5 Listas Doblemente Enlazadas Circulares
La cantidad de nodos que una lista enlazada puede contener esta limitada por la memoria del
computador.
TIPOS DE LISTAS.
52
Listas simplemente enlazadas circulares
Donde:
Cada elemento (nodo) de la lista simplemente enlazada debe tener dos campos:
• Un campo información (info) que contiene el valor de ese elemento y puede contener
cualquier tipo estándar o definido por el usuario.
• Un campo enlace (o puntero) que indica la posición del siguiente elemento de la lista.
NODO
53
Existe una marca para fin de lista, que es la constante NIL, también representada por una barra
inclinada o el signo eléctrico de tierra.
Type
pnodo = ^nodo
nodo = record
info : Integer;
Sig :pnodo;
End;
struct Nodo
{
int info;
struct Nodo *sig;
};
Como cualquier estructura de datos, en las listas simplemente enlazadas se puede realizar las siguientes
operaciones básicas:
• Inserción
• Eliminación
• Búsqueda
• Recorrido
RECORRIDO
Para realizar el recorrido en una lista simple, es necesario el uso de un puntero auxiliar ACT, con la
cual se visita toda la lista, empezando por la RAIZ y finalizando cuando el puntero ACT apunta a NIL.
ALGORITMO:
Recorrido(Dato:Integer)
Inicio
Actual = Raíz
Mientras(Actual <> Nil) hacer
Actual =Actual^.sig
54
Mostar Actual^.inf
Fin Mientras
Fin.
BUSQUEDA
Para realizar la búsqueda en una lista simple, es necesario el uso de un puntero auxiliar ACT, con la
cual se busca empezando por la RAIZ y finalizando cuando el puntero ACT apunta al elemento
buscado o a NIL.
ALGORITMO:
Buscar(Dato:Integer)
Inicio
Actual = Raíz
Mientras(Actual<>Nil) y (Actual^.inf<>Dato) hacer
Actual =Actual^.sig
Fin Mientras
Si Actual^.inf = Dato entonces
Mostar "El Dato Esta en la Lista"
Sino
Mostrar "No Existe el Dato"
Fin si
Fin.
INSERCION DE DATOS
Existen distintas formas de adición de nodos a una lista simple, de acuerdo a los propósitos de uso de
estos, entre las cuales se tiene:
55
Insertar(5)
• New(Nuevo)
• Nuevo^.inf= 5
• Raiz = nuevo (1)
• Nuevo^.sig= nil(2)
INSERTAR AL PRINCIPIO:
Insertar(3)
• New(Nuevo)
• Nuevo^.inf= 3
• Nuevo^.sig = Raiz(1)
• Raiz = Nuevo (2)
INSERTAR AL FINAL:
Insertar(13)
56
• New(Nuevo)
• Nuevo^.inf= 13
• ant^.sig =Nuevo(1)
• Nuevo^.sig =Nil(2)
Insertar(7)
• New(Nuevo)
• Nuevo^.inf= 7
• Nuevo^.sig =Act(1)
• Ant^.sig = Nuevo(2)
ALGORITMO :
Insertar (Dato:Entero)
Inicio
New(Nuevo)
Nuevo^.inf = Dato
Si Raiz = Nil Entonces
Raiz = Nuevo
Nuevo^ sig = Nil
Si no
Anterior = Raiz
Actual = Raiz
Mientras (Actual <>Nil) y (Dato> Actual^.Inf) Hacer
Anterior = Actual
Actual = Actual^.sig
Fin Mientras
Si Anterior =Actual Entonces
Nuevo^.sig = Raiz
Raiz = Nuevo
Si no
Nuevo^.sig = Actual
57
Anterior^.sig = Nuevo
Fin si
Fin si
Fin.
ELIMINAR DATOS
Para eliminar un dato de una lista simple, es necesario buscarlo en la lista primeramente.
La búsqueda se realiza con dos punteros auxiliares ANT y ACT, si el dato se encuentra en la lista, el
puntero ACT apunta a ella y el puntero ANT apunta al anterior elemento.
Eliminar(5)
• Raiz= act^.sig(1)
• Dispose(act)
Eliminar(9)
58
• ant^.sig = act^.sig
• Dispose(act)
ALGORITMO:
Eliminar (Dato:Entero)
Inicio
Si Raiz = nil then
Mostrar ‘No existe Datos’
Si no
Anterior = Raiz
Actual = Raiz
Mientras (Actual <> Nil) y (Actual^.inf <> Dato) Hacer
Anterior = Actual
Actual =Actual^.sig
Fin Mientras
Si Actual^.inf = Dato entonces
Si Anterior = Actual entonces
Raiz = Actual^.sig
Sino
Anterior^.sig = Actual^.sig
Fin si
Dispose(Actual)
Sino
Imprimir "No Existe el Dato"
Fin si
Fin si
Fin.
Esta lista presenta la gran ventaja de que cada nodo en una lista circular es accesible desde cualquier
nodo. Tiene el inconveniente de que requiere un diseño cuidadoso para evitar caer en un bucle infinito.
59
• Listas simples circulares sin nodo cabecera
El manejo de la primera es complejo, de tal modo que solo se usaremos las listas enlazadas con nodo
cabecera, debido a su fácil manejo.
Como cualquier estructura de datos, en las listas simplemente enlazadas circulares se puede realizar las
siguientes operaciones básicas:
• Inserción
• Eliminación
• Búsqueda
• Recorrido
RECORRIDO
Para realizar el recorrido en una lista simple circular, es necesario el uso de un puntero auxiliar ACT,
con la cual se visita toda la lista, empezando por la RAIZ y finalizando cuando el puntero ACT apunta
a NIL.
ALGORITMO:
Recorrido(Dato:Integer)
Inicio
Actual = Raíz^.sig
Mientras(Actual <> Raiz) hacer
Actual =Actual^.sig
Mostarc Actual^.inf
60
Fin Mientras
Fin.
BUSQUEDA
Para realizar la búsqueda en una lista simple, es necesario el uso de un puntero auxiliar ACT, con la
cual se busca empezando por la RAIZ y finalizando cuando el puntero ACT apunta al elemento
buscado o a NIL.
ALGORITMO :
Buscar(Dato:Integer)
Inicio
Actual = Raíz^.sig
Mientras(Actual <> Raiz) y (Actual^.inf <> Dato) hacer
Actual =Actual^.sig
Fin Mientras
Si Actual^.inf = Dato entonces
Mostar "El Dato Esta en la Lista"
Sino
Mostrar "No Existe el Dato"
Fin si
Fin.
INSERCION DE DATOS
Existen distintas formas de adición de nodos a una lista simple circular, de acuerdo a los propósitos de
uso de estos. Entre las cuales se tiene:
En todas ellas se realiza una tarea común, que es el de crear una nuevo nodo para almacenar al
elemento que será agregado a la lista.
Insertar(5)
61
• New(Cab)
• New(Nuevo)
• Nuevo^.inf= 5
• Raiz = Cab (1)
• Cab^.sig= nuevo (2)
• Nuevo^.sig= Raiz (3)
Insertar(7)
• New(Nuevo)
• Nuevo^.inf= 7
• Nuevo^.sig = Act (1)
• Ant^.sig = Nuevo (2)
ALGORITMO :
Insertar (Dato:Entero)
Inicio
New(Nuevo)
Nuevo^.inf = Dato
Si Raiz = Nil Entonces
New(Cab)
Raiz = Cab
Cab^.sig =Nuevo
Nuevo^.sig = Raiz
Si no
Anterior = Raiz
Actual = Raiz^ sig
Mientras (Actual <> Raiz) y (Dato> Actual^.Inf) Hacer
62
Anterior = Actual
Actual = Actual^.sig
Fin Mientras
Nuevo^.sig = Actual
Anterior^.sig = Nuevo
Fin si
Fin.
ELIMINAR DATOS
Para eliminar un dato de una lista simple circular, es necesario buscarlo en la lista primeramente. La
búsqueda se realiza con dos punteros auxiliares ANT y ACT, si el dato se encuentra en la lista, el
puntero ACT apunta a ella y el puntero ANT apunta al anterior elemento.
Eliminar(9)
Para eliminar el ultimo elemento, es necesario liberar la memoria de los nodos que apuntan a ANT y
ACT.
• Dispose(ant)
• Dispose(act)
• Raiz = nil (1)
63
Eliminar(5)
• ant^.sig = act^.sig
• Dispose(act)
ALGORITMO:
Eliminar (Dato:Entero)
Inicio
Si Raiz = nil then
Mostrar ‘No existe Datos’
Si no
Anterior = Raiz
Actual = Raiz^.sig
Mientras (Actual <> Raiz) y (Actual^.inf <> Dato) Hacer
Anterior = Actual
Actual =Actual^.sig
Fin Mientras
Si Actual^.inf <>Raiz entonces
Si Anterior = Actual^.sig entonces
Dispose(Anterior)
Raiz = Nil
Sino
Anterior^.sig = Actual^.sig
Fin si
Dispose(Actual)
Sino
Imprimir "No Existe el Dato"
Fin si
Fin si
Fin.
64
Estos enlaces(punteros) se utilizan para denotar la dirección del predecesor y sucesor de un nodo dado.
El predecesor se llama enlace izquierdo y el sucesor enlace derecho.
Donde:
NODO
Donde:
Existe una marca para fin de lista, que es la constante NIL, también representada por una barra
inclinada o el signo eléctrico de tierra.
Type
pnodo = ^nodo
nodo = record
info : Integer;
Ant, Sig :pnodo;
End;
Struct nodo
{
int info;
struct nodo *Ant, * Sig;
};
65
Inserción
Eliminación
Recorrido
Búsqueda
RECORRIDO
En una lista doble circular es posible realizar dos tipos de recorridos, esto debido a que en estas listas
existen enlaces en ambas direcciones.
Este recorrido se inicia cuando el puntero apunta a INICIO y se recorre de nodo a nodo con el puntero
SIG hasta que el nodo apunte a NIL.
ALGORITMO:
Inicio
Actual = Inicio
Mientras Actual <> NIL hacer
Mostrar Actual^.inf
Actual = Actual^.sig
Fin Mientras
Fin.
Este recorrido se inicia cuando el puntero apunta a FIN y se recorre de nodo a nodo con el puntero
ANT hasta que el puntero apunte a NIL.
ALGORITMO:
Inicio
Actual = Fin}
Mientras Actual <> Nil Hacer
Mostrar Actual^.inf
66
Actual = Actual^.ant
Fin Mientras
Fin.
INSERCION DE DATOS
Existen distintas formas de adición de nodos a una lista doble, de acuerdo a los propósitos de uso de
estos. Entre las cuales se tiene:
En todas ellas se realiza una tarea común, que es el de crear una nuevo nodo para almacenar al
elemento que será agregado a la lista.
Insertar(5)
• New(Nuevo)
• Nuevo^.inf= 5
• Inicio = nuevo (1)
• Fin = nuevo (2)
• Nuevo^.ant= nil (3)
• Nuevo^.sig= nil (4)
INSERTAR AL PRINCIPIO
Insertar(3)
67
• New(Nuevo)
• Nuevo^.inf= 3
• Inicio = Nuevo(1)
• Nuevo^.sig = Act(2)
• Act^.ant = Nuevo(3)
• Nuevo^.ant = Nil(4)
INSERTAR AL FINAL:
Insertar(13)
• New(Nuevo)
• Nuevo^.inf= 13
• ant^.sig = Nuevo(1)
• Nuevo^.sig = Nil(2)
• Fin = Nuevo(3)
• Nuevo^.ant = Ant(4)
68
Insertar(7)
• New(Nuevo)
• Nuevo^.inf= 7
• Ant^.sig = Nuevo(1)
• Nuevo^.sig = Act(2)
• Act^.ant = Nuevo(3)
• Nuevo^.ant = Ant (4)
ALGORITMO:
Insertar (Dato:Entero)
Inicio
New(Nuevo)
Si Inicio = Nil Entonces
Inicio = Nuevo; Final = Nuevo
Nuevo^.ant = Nil;
Nuevo^.sig = Nil
Si no
Anterior = Inicio
Actual = Inicio
Mientras (Actual <> Nil) y( Actual^.inf < Dato) Hacer
Anterior = Actual
Actual =Actual^.sig
Fin Mientras
Si Actual = Anterior Entonces
Nuevo^.sig = Inicio
Inicio^.ant = Nuevo
Inicio = Nuevo
Nuevo^.ant = Nil
Si no
Si Actual = Nil Entonces
Final = Nuevo
Si no
Actual^.ant = Nuevo
Fin si
Nuevo^.sig = Actual
Anterior^.sig = Nuevo
Nuevo^.ant = Anterior
Fin si
Fin si
Fin.
ELIMINAR DATOS
Para eliminar un dato de una lista doble, es necesario buscarlo en la lista primeramente.
69
La búsqueda se realiza con dos punteros auxiliares ANT y ACT, si el dato se encuentra en la lista, el
puntero ACT apunta a ella y el puntero ANT apunta al anterior elemento.
ELIMINACIÓN AL PRINCIPIO:
Eliminar(5)
1. Inicio = Inicio^.sig
2. Inicio^.ant = Nil
Dispose(act)
ELIMINACIÓN AL MEDIO:
Eliminar(8)
70
1. Ant^.sig = Act^.sig
2. Act^.sig^.ant = Ant
Dispose(act)
ELIMINACIÓN AL FINAL:
Eliminar (9)
ALGORITMO:
71
Fin si
Anterior^.sig = Actual^.sig
Fin si
Fin si
Fin.
Esta lista presenta la gran ventaja de que cada nodo en una lista doble circular es accesible desde
cualquier nodo. Tiene el inconveniente de que requiere un diseño cuidadoso para evitar caer en un
bucle infinito.
El manejo de la primera es complejo, de tal modo que solo se usaremos las listas enlazadas con nodo
cabecera, debido a su fácil manejo.
72
OPERACIONES EN LISTAS DOBLEMENTE ENLAZADAS CIRCULARES.
Inserción
Eliminación
Recorrido
Búsqueda
RECORRIDO
En una lista doble circular es posible realizar dos tipos de recorridos, esto debido a que en estas listas
existen enlaces en ambas direcciones.
Este recorrido se inicia cuando el puntero apunta a Raiz^.sig y se recorre de nodo a nodo con el puntero
SIG hasta llegar al nodo Raiz.
ALGORITMO:
Inicio
Actual = Raiz^.sig
Mientras Actual <> Raiz hacer
Mostrar Actual^.inf
Actual = Actual^.sig
Fin Mientras
Fin.
Este recorrido se inicia cuando el puntero apunta a Raiz^.ant y se recorre de nodo a nodo con el puntero
ANT hasta llegar al nodo Raiz.
73
ALGORITMO:
Inicio
Actual = Raiz^.ant
Mientras Actual <> Raiz Hacer
Mostrar Actual^.inf
Actual = Actual^.ant
Fin Mientras
Fin.
INSERCION DE DATOS
Existen dos formas de inserción de datos a una lista doblemente enlazada circular:
Insertar(9)
• New(Cab)
• New(nuevo)
• nuevo^.inf=9
• Raiz = Cab (1)
• Cab^.sig = Nuevo (2)
• Nuevo^.sig = Cab (3)
• Cab^.ant = Nuevo (4)
• Nuevo^.ant = Cab (5)
74
INSERTAR AL MEDIO DE DOS NODOS:
Insertar(6)
• New(Cab)
• New(nuevo)
• nuevo^.inf=6
• Ant^.sig = Nuevo(1)
• Nuevo^.sig = Act(2)
• Act^.ant = Nuevo(3)
• Nuevo^.ant = Ant(4)
ALGORITMO:
Insertar (dato:entero)
Inicio
New(Nuevo)
Nuevo^.inf = Dato
Si Raiz = Nil Entonces
New(NodoC)
Raiz = NodoC
NodoC^.sig = NodoC
NodoC^.ant = Nuevo
Nuevo^.ant = NodoC
Si no
Anterior = Raiz
Actual = Raiz^.sig
Mientras (Actual <> Raiz) y (Actual^.inf < dato) Hacer
Anterior = Actual
Actual =Actual^.sig
Fin Mientras
Nuevo^.sig = Actual
Actual^.sig = Nuevo
Nuevo^.ant = Anterior
Actual^.ant = Nuevo
Fin si
Fin.
75
ELIMINAR DATOS
Para eliminar un dato de una lista doble circular, es necesario buscarlo en la lista primeramente.
La búsqueda se realiza con dos punteros auxiliares ANT y ACT, si el dato se encuentra en la lista, el
puntero ACT apunta a ella y el puntero ANT apunta al anterior elemento.
Eliminar(9)
• Dispose(ant)
• Dispose(act)
• Raiz = Nil
ELIMINAR UN ELEMENTO
Eliminar(9)
76
• Ant^.sig = Act^.sig(1)
• Act^.sig ^.ant = Ant(2)
• Dispose(act)
ALGORITMO:
Eliminar (Valor:Entero)
Inicio
Anterior = Raiz
Actual = Raiz^.sig
Mientras (Actual <> Raiz) y (Actual^.inf <> Valor) Hacer
Anterior = Actual
Actual =Actual^.sig
Fin Mientras
Si (Actual^.inf <> Valor) Entonces
Mostrar "No Existe el Elemento"
Si no
Si Actual^.sig = Anterior) Entonces
Dispose(Anterior)
Raiz = Nil
Si no
Anterior^.sig = Actual^.sig
Actual^.sig ^.ant= Anterior
Fin si
Dispose(Actual)
Fin si
Fin.
77
TEMA 6
ÁRBOLES
OBJETIVOS
Desarrollar aplicaciones con estructuras ramificadas que optimizan las operaciones básicas.
Encontrar nuevas formas de organización de datos en forma ramificada de acuerdo a las características
de la aplicación.
CONTENIDO
6.1 Introducción
6.2 Conceptos Asociados
6.3 Árbol Binario de Búsqueda
6.4 Árboles Equilibrados o AVL
6.1 DEFINICION.
Un árbol es una lista en la que cada uno de sus elementos apunta a uno, ninguno o varios elementos del
mismo tipo.
Un árbol es una estructura dinámica y homogénea, por tanto está compuesto de elementos del mismo
tipo base T, de forma que la estructura puede estar vacía o compuesta por un elemento del tipo base T
del que cuelgan un número finito de estructuras árbol similar, a las que llamaremos subárboles, y entre
sí son disjuntos.
Dos árboles son iguales cuando tengan igual número de nodos, igual contenido en ellos y, además,
estén dispuestos de igual manera
REPRESENTACIÓN
Un árbol es una estructura de datos no lineal que establece una jerarquía entre sus elementos.
Matriz de adyacencia.
Lista de adyacencia.
78
Estructura dinámica pura.
MATRIZ DE ADYACENCIA
Es un array [1..n,1..n] OF BOOLEAN donde n es el número de nodos que tiene el árbol y cada posición
de la matriz indicará si existe un enlace entre dos nodos. Esto es normal hacerlo en lenguajes en que no
pueden crearse componentes dinámicamente, y referenciales por medio de punteros.
Ejemplo:
LISTA DE ADYACENCIA
Es una tabla de 1 a n, siendo n el número de nodos, donde cada elemento de la tabla representa cada
uno de los nodos del árbol y de cada uno de los elementos sale un puntero a una lista que está formada
por todos los nodos que están enlazados con él.
Ejemplo:
PNodo=^Nodo;
Nodo=RECORD
Info:Tinfo;
Enlace1,..., EnlaceN: PNodo;
END;
79
6.2 CONCEPTOS ASOCIADOS
A continuación veremos algunos conceptos asociados a los Arboles.
Rama: Es cada uno de los enlaces que existe entre los nodos de un árbol.
Raíz: Es aquel nodo que no tiene antecesores, es decir, todos son descendientes directos o indirectos
de el.
Subárbol: Se llama subárbol de raíz m al conjunto de nodos que dependen directa o indirectamente
de él, así como al propio m.
Antecesor o padre: Es un nodo del que cuelga algún otro, llamado descendiente o hijo.
Grado de un nodo: Es el número de descendientes directos que tiene, el grado de un árbol es igual
al del nodo con mayor grado.
Nivel : Es el número de ramas que hay que recorrer para llegar a él desde la raíz, tendiendo en cuenta
que la raíz tiene nivel uno.
80
Nodo terminal u hoja: Es aquel que tiene grado cero, es decir, que no tiene ningún descendiente.
Longitud de camino interno de un arbol: Es la suma de las longitudes de camino de todos sus
nodos.
LCI = 1+2+2+3+3+3 = 14
Longitud de camino externa: Es la suma de las longitudes de camino de todos los Nodos
Especiales (Es aquel que se hace colgar de aquellos nodos que no tienen completo el cupo de
descendientes).
LCE = 3+4+4+4+4+4+4 = 27
Longitud de camino interna media: Es la longitud de camino interna partido del número de
nodos
Se define un árbol binario como un conjunto finito de elementos (nodos) que bien está vació o está
formado por una raíz con dos árboles binarios disjuntos, llamados subárbol izquierdo y derecho de la
raíz.
81
En los apartados que siguen se considerarán únicamente árboles binarios y, por lo tanto, se utilizará la
palabra árbol para referirse a árbol binario. Los árboles de grado superior a 2 reciben el nombre de
árboles multicamino.
DEFINICION
Los árboles binarios se utilizan frecuentemente para representar conjuntos de datos cuyos elementos se
identifican por una clave única.
Si el árbol está organizado de tal manera que la clave de cada nodo es mayor que todas las claves del
subárbol izquierdo, y menor que todas las claves del subárbol derecho se dice que este árbol es un árbol
binario de búsqueda.
Ejemplo:
OPERACIONES BASICAS
Una tarea muy común a realizar con un árbol es ejecutar una determinada operación con cada uno de
los elementos del árbol. Esta operación se considera entonces como un parámetro de una tarea más
general que es la visita de todos los nodos o, como se denomina usualmente, del recorrido del árbol.
Si se considera la tarea como un proceso secuencial, entonces los nodos individuales se visitan en un
orden específico, y pueden considerarse como organizados según una estructura lineal.
o Recorrido en amplitud.
o Recorrido en profundidad.
RECORRIDO EN AMPLITUD.
En el recorrido por amplitud se visitan los nodos por niveles. Para ello se utiliza una estructura auxiliar
tipo "cola" en la que después de mostrar el contenido del nodo, empezando por el nodo raíz, se
almacenan los punteros correspondientes a sus hijos izquierdo y derecho.
De esta forma si recorremos los nodos de un nivel, mientras mostramos su contenido, almacenamos en
la cola los punteros a todos los nodos del nivel siguiente.
82
Resultado:
El algoritmo:
Amplitud
Inicio
Ptr = raiz
Cima = 1
Inicio=1
Pila[cima] = Ptr
ptr=Pila[Inicio]
inicio++
Mostrar ptr^.inf
Fin_mientras
Fin.
RECORRIDO EN PROFUNDIDAD.
Para visualizar o consultar los datos en un árbol se necesita recorrer el árbol. Al contrario que las listas
enlazadas, los árboles no tienen un primer valor, un segundo valor, etc.
Existen métodos de recorrido de un árbol binario, de acuerdo al orden en que se visitan los nodos, de
forma que será preciso elegir cuidadosamente el tipo de recorrido. Según sea la estrategia a seguir, los
recorridos se conocen como:
Recorrido inorden
Recorrido preorden
Recorrido postorden
83
Las tres etapas básicas en el recorrido de un árbol binario recursivamente son:
RECORRIDO PREORDEN
En el árbol, los nodos se han numerado en el orden que son visitados en el recorrido preorden
· Después se visita el subárbol izquierdo. Este consta de los nodos (B, D y E), por lo que siguiendo con
el orden (Raiz, izq., der.), se visita primero B, luego D y por ultimo E.
· Por ultimo se visita el subárbol derecho que consta de los nodos (C, F y G). Siguiendo el orden (Raiz,
izq., der.), se visita primero C, luego F y por ultimo G.
84
Preorden
Inicio
Cima = 1
Pila[cima] = nil
Ptr = raiz
Mostrar ptr^.inf
Fin_mientras
Fin.
RECORRIDO INORDEN
Si el árbol no esta vació, el método implica los siguientes pasos:
En el árbol, los nodos se han numerado en el orden que son visitados en el recorrido enorden.
85
· El primer recorrido es el subárbol izquierdo del nodo raíz. Este consta de los nodos (B, D y E), por lo
que siguiendo con el orden (Izq, Raiz, Der), se visita primero D, luego B y por ultimo E.
· Por ultimo se visita el subárbol derecho que consta de los nodos (C, F y G). siguiendo el orden (Izq,
Raiz, Der), se visita primero F, luego C y por ultimo G.
Ptr = pila[cima]
Cima=cima-1
Tieneder =false
86
inicio
si p <> nil entonces
postorden (p^.izq)
postorden (p^.der)
escribir p^.inf
fin si
fin
En el árbol, los nodos se han numerado en el orden que son visitados en el recorrido preorden.
· Primero se visita el subárbol izquierdo. Este consta de los nodos (B, D y E), por lo que siguiendo con
el orden (Izq, Der, Raiz), se visita primero D, luego E y por ultimo B.
· Por ultimo se visita el subárbol derecho que consta de los nodos (C, F y G). Siguiendo el orden (Izq,
Der, Raiz), se visita primero F, luego G y por ultimo C.
Ptr = pila[cima]
Cima = cima-1
Salir = false
87
Mientras ptr <> nil y no salir hacer
Si prt > 0 entonces
Mostrar prt^.inf
Ptr = pila[cima]
Cima = cima-1
Sino
Ptr = -ptr
Salir=true
Fin_si
Fin_mientras
Fin_mientras
Fin.
INSERCIÓN DE DATOS
La inserción de datos en un arbol binario de búsqueda, se realiza de acuerdo al valor que se debe
insertar, si el dato es menor que la raíz es insertada en el subárbol izquierdo, si el dato es mayor que la
raíz es insertada en el subárbol derecho.
Insertar(Var raiz:pnodo;dato:entero)
Inicio
Si raiz = nil entonces
New(raiz)
Raiz^.inf = dato
Raiz^.izq = nil
Raiz^.der = nil
Sino
Si dato > raiz^.inf entonces
Insertar (raiz^.der,dato)
Sino
Insertar(raiz^.izq,dato)
Fin_si
Fin_si
Fin.
ELIMINAR UN DATO
88
Solución : Ptr = ptr^.der
En este caso existen dos posibilidades de reemplazo, donde el nodo a ser eliminado puede ser
reemplazado por los siguientes nodos:
caso a).- El nodo del subárbol izquierdo que se encuentra mas a la derecha
caso b).- El nodo del subárbol derecho que se encuentra mas a la izquierda
89
ALGORITMO :
Eliminar(dato:integer;Var ptr:pnodo)
Inicio
Si ptr = nil entonces
Mostrar "no existe el elemento"
Sino
Si dato > ptr^.inf entoces
Eliminar (dato,ptr^.der)
Sino
Si dato < ptr^.inf entonces
Eliminar(dato,ptr^.izq)
Sino
Aux =ptr
Si aux^.izq=nil entonces
Ptr =aux^.der
Sino
Si aux^.der = nil entonces
Ptr=aux^.izq
Sino
Reemplazar(aux^.izq)
Fin_si
Fin_si
Free(aux)
Fin_si
Fin_si
Fin_si
Fin.
Reemplazar(Var Ader:pnodo)
Inicio
Si Ader^.der <> nil entonces
Reeplazar(Ader^.der)
Sino
Ptr^.inf = Ader^.inf
aux = Ader
Ader = Ader^.izq
Fin_si
Fin.
BÚSQUEDA DE DATOS
90
matemáticas se pueden escribir según diversos tipos de notaciones. La notación infija es la empleada
habitualmente y requiere el uso de paréntesis, pero únicamente cuando es necesario modificar la
prioridad entre los distintos operadores.
Para construir se utilizaran, como estructura de datos auxiliares, una pila de punteros a los nodos de
un árbol y otra pila de operadores para retenerlos temporalmente hasta que llegue el momento de
incorporarlos al árbol. Los pasos a seguir son lo siguientes.
1. Cuando se lee un OPERANDO se crea un árbol de un nodo y se mete el apuntador a el en
correspondiente pila.
2. Cuando se lee un OPERADOR se retiene en la pila de operadores. Los operadores se van poniendo
en esta pila hasta encontrar uno con mayor o igual prioridad, en cuyo caso se sacan los que hubiera
en la pila de mayor o igual prioridad y se coloca en ella este ultimo operador leido.
3. Cuando se lee un PARÉNTESIS IZQUIERDO se retiene en la pila de operadores.
4. Cuando se lee un PARÉNTESIS DERECHO se sacan los OPERADORES que hubiera en la pila de
operadores hasta encontrar el paréntesis izquierdo.
5. El proceso termina cuando se acaba la entrada y la pila de operadores queda vacía.
Nota: Al sacar de la pila de operadores uno de ellos, extraer de la pila de punteros los dos últimos
apuntadores. Con estos tres elementos, se forma un nuevo árbol cuya raíz almacena el operador y
los punteros anteriores. El apuntador a este nuevo árbol se coloca ahora en la pila de apuntadores.
1.-
91
3.- El + saca de la pila todos los operadores con mayor prioridad que el y l continuación se coloca el
4.-
5.-
92
búsqueda en el que para cada nodo, las alturas de sus subárboles izquierdo y derecho no difieren en
más de 1.
El algoritmo para mantener un árbol AVL equilibrado se basa en reequilibrados locales, de modo que
no es necesario explorar todo el árbol después de cada inserción o borrado.
DEFINICIÓN
En un árbol AVL, se pueden realizar en O(lon n) unidades de tiempo, incluso en el peor de los casos,
las siguientes operaciones:
Vamos a añadir un campo nuevo a la declaración del tipo TArbol, que se llamará factor de equilibrio
(equ), este factor de equilibrio (equ) es la diferencia entre las alturas del árbol derecho y el izquierdo:
Por definición, para un árbol AVL, este valor debe ser -1, 0 ó 1.
INSERCIÓN EN AVL
La inserción se hace siempre en las hojas, y vamos a tener un campo de equilibrio, además una variable
global llamada crecido de tipo BOOLEAN, que en el momento de insertar lo vamos a poner a TRUE
para después controlar si se ha desequilibrado o no.
Se sube restando o sumando 1 hasta llegar a un cero. Si a la hora de sumar o restar un uno se sale del
rango hay que reequilibrar.
DESEQUILIBRIOS Y REEQUILIBRIOS
Al insertar un nuevo nodo en un árbol equilibrado se pueden producir desequilibrios, que quedarán
representados en los casos mostrados a continuación.
93
En las figuras, las cajas rectangulares representan subárboles, y la altura añadida por la inserción se
indica con cruces. Simples transformaciones restauran el equilibrio deseado.
Dado el siguiente Arbol Balanceado, donde se observa que el nodo B ya se encuentra crecido en 1 nivel
en su subarbol izquierdo.
Se añade al subarbol izquierdo del nodo A un nuevo dato "X", lo que causa un desequilibrio en el
arbol, ya que el subarbol izquierdo del nodo B se encuentra crecido ahora en 2 niveles.
Se corrige con la rotación izquierda-izquierda simple, que consiste en subir el nodo A, que tendrá al
final el campo de equilibrio a 0.
94
Se reequilibra con rotación derecha-derecha simple. Es el reflejado del anterior.
La figura muestra la situación antes y después de la rotación simple, donde el elemento X fue insertado
en E, y b corresponde al nodo N. Antes de la inserción, la altura de N es la altura de C+1. Idealmente,
para recuperar la condición de balance se necesitaria bajar A en un nivel y subir E en un nivel, lo cual
se logra cambiando las referencias derecha de b e izquierda de d, quedando este último como nueva
raíz del árbol, N'.
Nótese que ahora el nuevo árbol tiene la misma altura que antes de insertar el elemento, C+1, lo cual
implica que no puede haber nodos desbalanceados más arriba en el árbol, por lo que es necesaria una
sola rotación simple para devolver la condición de balance al árbol. Nótese también que el árbol sigue
cumpliendo con la propiedad de ser ABB.
Dado el siguiente Arbol Balanceado, donde se observa que el nodo B ya se encuentra crecido en 1 nivel
en su subarbol izquierdo.
Se añade al subarbol derecho del nodo A un nuevo dato "X", lo que causa un desequilibrio en el arbol,
ya que el subarbol izquierdo del nodo B se encuentra crecido ahora en 2 niveles.
95
Se corrige con la rotación izquierda-derecha compuesta. Se sube el nodo C que pasa a tener el campo
de equilibrio a 0.
96
Para el caso de la figura, la altura de N antes de la inserción era G+1. Para recuperar el balance del
árbol es necesario subir C y E y bajar A, lo cual se logra realizando dos rotaciones simples: la primera
entre d y f, y la segunda entre d, ya rotado, y b, obteniéndose el resultado de la figura. A este proceso
de dos rotaciones simples se le conoce como rotación doble, y como la altura del nuevo árbol N' es la
misma que antes de la inserción del elemento, ningún elemento hacia arriba del árbol queda
desbalanceado, por lo que solo es necesaria una rotación doble para recuperar el balance del árbol
después de la inserción. Nótese que el nuevo árbol cumple con la propiedad de ser ABB después de la
rotación doble.
A continuación se simulan las inserciones de nodos en un árbol de búsqueda equilibrado, partiendo del
árbol vació. Por comodidad se supone que el campo clave es entero. El factor de equilibrio actual de un
nodo y el nuevo al añadir un nodo se representan como superíndices de los nodos. Los punteros n, nl y
n2 referencia al nodo que viola la condición de equilibrio y a los descendientes en el camino de
búsqueda.
Una vez insertado el nodo con la clave 29, al regresar por el camino de búsqueda cambia los factores de
equilibrio, así el del nodo 45 pasa a -1, y en el nodo 68 se pasa a -2. Se ha roto el criterio de equilibrio y
debe de reestructurarse. Al ser los factores de equilibrio -l y -2 deben de realizarse una rotación de los
nodos II para rehacer el equilibrio. Los movimientos de los punteros para realizar esta rotación II
n^.izqdo = n1^.drcho
n1^.drcho = n
n = n1
Realizada la rotación, los factores de equilibrio serán siempre O en las rotaciones simples. El árbol
queda de la forma siguiente:
97
Inserción de las claves 75 y 90
Una vez insertado el nodo con la clave 90, a la derecha del nodo 75, y regresar por el camino de
búsqueda para así calcular los nuevos factores de equilibrio, se observa que dicho factor queda
incrementado en 1 pues la inserción ha sido por la derecha. En el nodo con clave 68 queda roto el
equilibrio. Para reestructurar se realiza una rotación DL Los movimientos de los punteros para realizar
esta rotación DD:
Una vez realizada la rotación, los factores de equilibrio de los nodos implicados será 0, como ocurre en
todas las rotaciones simples, el árbol queda como sigue:
Insertamos la clave 70
98
para insertar el nodo con la clave 70 se sigue el camino : Derecha de 45 izquierda de 75 y se inserta por
la derecha al nodo 68. al regresar por el camino de búsqueda.
Queda, los factores de equilibrio se incrementan en 1 si se fue por la rama derecha, se decrementa en
¡ si se fue por la rama izquierda. En el nodo 45 cl balanceo se ha roto. La rotación de los nados para
reestablecer el equilibrio es DI. Los movimientos de los punteros para realizar esta rotación DI
Los factores de equilibrio de los nodos implicados en la rotación dependen del valor antes de la
inserción del nodo referenciado por n2 según esta tabla:
Inserción de la clave 34
99
El camino seguido para insertar el nodo con clave 34 ha seguido el camino de izquierda dc 68,
izquierda de 45, derecha de 29. Al regresar por el camino de búsqueda, el factor de equilibrio del nodo
29 se incrementa en 1 por seguir el camino de la rama derecha, el del nodo 45 se decrementa en 1 por
seguir la rama izquierda y pasa a ser -2, se ha roto el criterio de equilibrio. La rotación de los nodos
para reestablecer el equilibrio es ID.
Los factores de equilibrio de los nodos implicados en la rotación dependen del valor antes de la
inserción del nodo referenciado por n2, según esta tabla:
IMPLEMENTACIÓN DE LA INSERCIÓN
100
Un algoritmo que inserte y reequilibre dependerá en forma crítica de la forma en que se almacene la
información relativa al equilibrio del árbol. Una solución consiste en mantener la información sobre el
equilibrio, de forma completamente implícita, en la estructura misma del árbol. En este caso, sin
embargo, el factor de equilibrio debe "averiguarse" cada vez que se encuentre afectado por una
inserción, con el consiguiente trabajo resultante.
Otra forma (la que vamos a usar) consiste en atribuir a, y almacenar con, cada nodo un factor de
equilibrio explícito. La definición de nodo se amplía entonces a:
TYPE Nodo=RECORD
Clave:Tclave;
Info:Tinfo;
Izq,Der:^Nodo;
Equi:-1..1
END
El proceso de inserción de un nodo consta fundamentalmente de las tres partes consecutivas siguientes:
· Seguir el camino de búsqueda hasta que se comprueba que la clave aún no está en el árbol.
· Insertar el nuevo nodo y determinar el factor de equilibrio resultante.
· Volver siguiendo el camino de búsqueda y comprobar el factor de equilibrio de cada nodo.
Aunque este método realiza algunas comprobaciones que son redundantes, una vez que se establece el
equilibrio, éste no necesita comprobarse en los antecesores del nodo en cuestión.
En cada paso se debe mandar información sobre si la altura del subárbol (en el cual se ha realizado la
inserción) ha aumentado o no. Por lo tanto se extiende la lista de parámetros del procedimiento con el
BOOLEAN Crecido, que debe ser un parámetro de tipo variable, ya que se utiliza para transmitir un
resultado.
101
case p^.equi of
+1: begin
p^.equi:=0;
h:= false;
end;
0: p^.equi:=-1;
-1: begin {reequilibrar}
p1 := p^.izq;
if pl^.equi = -1 then
begin { rotacion II simple }
p^.izq := pl^.der;
p1^.der:=p;
p^.equi=0;
p := p1;
end
else
begin { rotación ID doble}
p2 := p1^.der;
p1^.der := p2^.izq;
p2^.izq := p1;
p^.izq := p2^.der;
p2^.der := p;
if p2^.equi = -1 then p^.equi := +1 else p^.equi :=0;
if p2^.equi = +1 then p1^.equi := -1 else p1^.equi :=0;
p := p2;
end;
p^.equi := 0;
h := false;
end;
end
end
else
if dato > p^.inf then
begin
insertar (dato, p^.der, h);
if h then {la rama derecha ha crecido}
case p^.equi of
-1: begin
p^.equi:=0;
h:=false;
end;
0: p^.equi :=+1;
+1: begin {reequilibrar}
p1 := p^.der;
if p1^.equi = +1 then
begin {rotacion DD simple}
p^.der := p1^.izq;
p1^.izq := p;
p^.equi := 0;
p := p1;
end
else
begin { rotación DI doble }
p2 := p1^.izq;
p1^.izq := p2^.der;
p2^.der := p1;
p^.der := p2^.izq;
p2^.izq := p;
if p2^.equi = +1 then p^.equi := -1 else p^.equi := 0;
102
if p2^.equi = -1 then p1^.equi := +1 else p1^.equi := 0;
p := p2;
end;
p^.equi := 0;
h := false;
end;
end
end
else
begin
p^.contador := p^.contador +1;
h:=false;
end;
end;
end;
BORRADO EN AVL
Vamos a ver solo las distintas posibilidades que se pueden dar al borrar un nodo en el lado derecho. A
la izquierda es simétrico.
EQUILIBRIOS Y DESEQUILIBRIOS
CASO 1. RAIZ.
Caso 1.1: Si alguno de los subárboles que salen de la raíz está vació, entonces el otro estará vació o
solo tiene un nodo, por ser un árbol equilibrado.
Caso 1.2: Si no hay ningún subárbol vació se sube el nodo de más a la derecha del subárbol izquierdo,
se intercambia los valores de la raíz por los de ese nodo, y después es borra este último.
Caso 2.1: Si el campo de equilibrio tiene un cero, los dos subárboles son iguales. Entonces lo borramos
y el campo de equilibrio pasa a -1.
Caso 2.2: Si tiene un 1, entonces el subárbol derecho tiene una altura más que el izquierdo. Al borrar
equilibramos y pasa a ser 0 ya que restamos 1.
Se puede haber desequilibrado por la izquierda porque al borrar se ha disminuido en uno la altura del
árbol.
103
Caso 2.3: Si tiene un -1, la altura del subárbol izquierdo es mayor que la del derecho. Al borrar en el
derecho se rompe el equilibrio, que pasa a -2.Hay tres casos.
Caso 2.3.1
Caso 2.3.2
Caso 2.3.3
104
Mediante rotación izquierda-derecha compuesta queda:
Hay otros dos casos, que el bloque 2'2 sea el más pequeño, o que lo sea el 2'1.Tienen altura N-2 y por
lo demás se tratan igual.
Una vez eliminado el nodo siguiendo los criterios establecidos anteriormente, se regresa por el camino
de búsqueda calculando los nuevos factores de equilibrio (Fe) de los nodos visitados. Si en alguno de
los nodos se viola el criterio de equilibrio, debe de restaurarse el equilibrio.
En el algoritmo de inserción, una vez que era efectuada una rotación el proceso terminaba ya que los
nodos antecedentes mantenían el mismo factor de equilibrio. En la eliminación debe de continuar el
proceso puesto que se puede producir más de una rotación en el retroceso realizado por el camino de
búsqueda, pudiendo llegar hasta la raíz del árbol.
En los procedimientos se utiliza el argumento boolean hh, será activado cuando la altura del subárbol
disminuya debido a que se haya eliminado un nodo, o bien porque al reestructurar haya quedado
reducida la altura del subárbol.
105
En el árbol de la figura va a ser eliminado el nodo con la clave 42: al ser un nodo hoja el borrado es
simple, se suprime el nodo. Al volver por el camino de búsqueda para determinar los Fe, resulta que el
Fe del nodo con clave 39 pasaría a ser -2 ya que ha decrementado la altura de la rama derecha, es
violado el criterio de equilibrio. Hay que reestructurar el árbol de raíz 39
El árbol resultante es
Ahora se elimina el nodo con la clave 21. Al tener dos ramas, toma el nodo más a la derecha de la rama
izquierda que es el de clave 11. Al volver por el camino de búsqueda para calcular los Fe, el factor de
equilibrio del nodo 11 pasaría a ser 2 y por tanto hay reestructurar el árbol de raíz II.
106
Rotación ID por que
N^.fe ß (1+1) y
n1^.fe < 0
El árbol resultado es :
En estos dos ejemplos se observa que después de realizar la eliminación de un nodo, y cuando se
regresa por el camino de búsqueda, el factor de equilibrio del nodo visitado disminuye en 1 si la
eliminación se hizo por su rama derecha y se incrementa en 1 si la eliminación se hizo por su rama
izquierda. Consideremos ahora este árbol equilibrado:
Se elimina el nodo de clave 25. Como es un nodo hoja se suprime. La supresión se hace por la rama
izquierda, por lo que la altura de la rama derecha correspondiente aumenta en 1, y lo mismo ocurre con
el factor de equilibrio.
107
Los factores de equilibrio quedan:
Al seguir regresando por el camino de búsqueda, el nodo raíz debe de incrementar su Fe con lo que
pasaría a +2, por consiguiente hay que restaurar el árbol, la rotación es derecha-izquierda ya que
108
IMPLEMENTACIÓN DE LA ELIMINACIÓN
109
procedure Rotacionii(var N: Ftrae; N1: Ptrae);
begin
N^.Izqdo:= N1^.Drcho;
N1^.Drcho:=N;
if N1^.Fe= -1 then
begin
N^.Fe :=0;
N1^.Fe := 0;
end
else
begin
N^.Fe :=-1;
N1^.Fe := 1
end;
N := N1;
end;
110
En el procedimiento Equilibnar2 al disminuir la altura de la rama derecha, el factor de equilibrio queda
decrementado en 1. De producirse una violación del criterio de equilibrio, la rotación será del tipo
izquierda-izquierda, o izquierda-derecha.
begin
111
if not ArbolVacio( R ) then
if x < R^.info then
begin
borrar_balanceado(R^.lzqdo,hh , x):
if hh then
Equilibrarl.(R, hh)
end
else
if x>R^.info then begin
borrar_balanceado(R^.Drcho.,hh ,x)
if hh then
Equilibrar2(R, Hh)
end
else
begin {ha sido encontrado el nodo}
q:= R;
if q^.Drcho= nil then
begin
R:= q^.Izqdo;
hh:= true{Disminuye la altura}
end
else
if q ^.Izqdo=nil then
begin
R:=q^.drcho;
hh:= true
end
else
begin
bor(q^.Izqdo,hh);
if hh then
Equilibrar1(R, hh)
end;
dispose(q);
end;
end;
112
ÁRBOLES BINARIOS DE EXPRESIONES
Para construir se utilizaran, como estructura de datos auxiliares, una pila de punteros a los nodos de
un árbol y otra pila de operadores para retenerlos temporalmente hasta que llegue el momento de
incorporarlos al árbol. Los pasos a seguir son lo siguientes.
6. Cuando se lee un OPERANDO se crea un árbol de un nodo y se mete el apuntador a el en
correspondiente pila.
7. Cuando se lee un OPERADOR se retiene en la pila de operadores. Los operadores se van poniendo
en esta pila hasta encontrar uno con mayor o igual prioridad, en cuyo caso se sacan los que hubiera
en la pila de mayor o igual prioridad y se coloca en ella este ultimo operador leido.
8. Cuando se lee un PARÉNTESIS IZQUIERDO se retiene en la pila de operadores.
9. Cuando se lee un PARÉNTESIS DERECHO se sacan los OPERADORES que hubiera en la pila de
operadores hasta encontrar el paréntesis izquierdo.
10. El proceso termina cuando se acaba la entrada y la pila de operadores queda vacía.
Nota: Al sacar de la pila de operadores uno de ellos, extraer de la pila de punteros los dos últimos
apuntadores. Con estos tres elementos, se forma un nuevo árbol cuya raíz almacena el operador y
los punteros anteriores. El apuntador a este nuevo árbol se coloca ahora en la pila de apuntadores.
1.-
113
2.- El ) saca los operadores de la pila hasta el (
3.- El + saca de la pila todos los operadores con mayor prioridad que el y l continuación se coloca el
4.-
5.-
114
TEMA 7
GRAFOS
OBJETIVOS
CONTENIDO
7.1 Definiciones
7.2 Almacenamiento de un Grafo en Memoria
7.3 Aplicaciones
Los grafos son estructuras de datos, utilizadas comúnmente en el manejo de redes, en la construcción
de circuitos eléctricos, en las estrategias de ventas, en el área de economía, cartografía y otras muchas
áreas del conocimiento.
7.1 DEFINICIONES.
7.1.1 Grafo
Un grafo es una estructura de datos compuesta por vértices y arcos. gráficamente un grafo se puede ver
así:
Un arco une dos vértices adyacentes. Por ejemplo el arco JK o el arco EG o GE. No se puede hablar del
arco AC pero si del AB y del BC.
115
Un grafo dirigido o dígrafo, es aquel en el que sus arcos tienen una orientación, por ejemplo:
7.1.3 Adyacencia
Se dice que existe adyacencia entre dos vértices si están unidos por un arco. También se dice que estos
vértices son adjuntos. Por ejemplo para el siguiente grafo:
los vértices A y B son adyacentes. En el caso de dígrafos, la adyacencia se expresa desde o hacia.
Como en el siguiente ejemplo:
116
Figura 7.4 Adyacencia en un dígrafo
Para este caso, se dice que A es adyacente hacia B, y D es adyacente hacia E, A es adyacente desde C y
a su vez D desde B. Para simplificar se emplea la siguiente notación:
7.1.4 Incidencia
Los arcos inciden en los vértices. Un arco es incidente en un vértice, si una de sus puntas llega a ese
vértice. Por ejemplo:
El arco a, es incidente en A y B.
El arco a es incidente en B. Este arco no es incidente en A ya que ese arco sale de A, y nunca llega a A.
Un grafo está fuertemente conectado si desde cualquier vértice se puede llegar a todos los demás; o si
desde cualquier vértice, se pueden visitar todos los demás. Por ejemplo:
117
Figura 7.8 Grafo fuertemente conectado
De la misma manera, un dígrafo está fuertemente si desde cualquier vértice se pueden visitar todos los
demás. Por ejemplo:
Desde los vértices A,B, C, D y E se puede llegar a todos los otros vértices. Por ello este dígrafo está
fuertemente conectado.
Un grafo o dígrafo es débilmente conectado, si por lo menos desde un vértice no podemos llegar a los
demás. Esta propiedad la cumple el siguiente dígrafo:
En este caso desde B no se puede llegar a ningún otro nodo, por ello es un dígrafo débilmente
conectado.
118
Se presenta cuando a partir de cualquier vértice, se puede recorrer la estructura, sin repetir ningún
vértice ni ningún arco. Por ejemplo:
Partiendo del vértice C, se puede recorrer todo el grafo, sin repetir vértices ni arcos.
Es un camino cerrado que recorre todos los arcos del grafo. Es decir se pueden visitar los vértices o
nodos cuantas veces sea necesario, pero los arcos solo se pueden recorrer una vez.
Ejemplo:
El recorrido desde el vértice A puede ser : ACDEFGBEHDAHBA. Como se puede observar los nodos
D, E, B y H son visitados dos veces, el nodo A es visitado 3 veces pero todos los arcos son recorridos
una sola vez.
Si partiendo de cualquier vértice , se pueden recorrer todos los vértices sin repetir ninguno, y
finalmente se llega al vértice origen. En el grafo anterior se encuentra el siguiente circuito
Hamiltoniano : A C D E F G B H A.
Es el número de arcos que inciden en un vértice. Para los dígrafos pueden existir para cada vértice dos
tipos de grado:
• GRADO DE ENTRADA
119
• GRADO DE SALIDA
Los grados de entrada de un vértice en un dígrafo, se determinan por el número de arcos que inciden en
el vértice y los grados de salida se determinan por el número de arcos que parten del vértice.
Para la figura anterior, los grados de entrada y salida para cada vértice son:
GRADO- GRADO-
VERTICE
ENTRADA SALIDA
A 2 0
B 2 0
C 1 3
D 0 3
E 2 1
Un grafo se dice que es regular , si todos los vértices tienen el mismo grado. Por ejemplo:
120
7.1.12 Arco Cíclico
Ejemplo:
Un grafo es simple, si no tiene arcos cíclicos y existe un solo arco entre dos vértices.
Un grafo es completo si cada vértice tiene un grado igual a n-1, donde n es el número de vértices que
componen el grafo. Por ejemplo los siguientes grafos son completos:
121
7.2 ALMACENAMIENTO DE UN GRAFO EN MEMORIA.
Existen varias formas de almacenar estas estructuras en memoria.
Para almacenar un grafo en una lista de adyacencia, debemos trabajar con un arreglo de listas. Cada
una de estas listas almacena los adjuntos a un vértice dado, comenzando por los vértices de más arriba
y los de más a la izquierda como orden de prioridad.
Por ejemplo una lista tendrá almacenados todos los adjuntos al vértice E; otra lista tendrá almacenados
todos los adjuntos al vértice I, etc. Esta es la lista de adyacencia para el grafo anterior (figura 7.19):
122
7.2.1.1 Almacenamiento en memoria. Grafo es un arreglo de estructuras de tipo nodo. Aquí la
estructura nodo está conformada por dos campos. El primero es el campo indice que es el identificador
de un nodo. El segundo es el campo siguiente que es un apuntador a otra estructura de tipo nodo.
INICIAR_GRAFO ( nv)
Inicio
i=1
Mientras ( i <= nv)
Ind[i]= 0
Sig[i]=A
i=i + 1
Fin-Mientras
Fin
El algoritmo Leer_grafo se encarga de leer el índice de cada vértice y sus correspondientes adjuntos
(que forman una lista) para así formar el grafo.
LEER_GRAFO(nv)
Inicio
INICIAR_GRAFO(nv)
i =1
Mientras ( i <= nv) Ejecute
nad =NumAdjuntos[i]
j =1
Mientras( j <= nad) Ejecute
ad =Lea(adjunto)
ins _ grafo (grafo, i, ad )
j=j + 1
Fin-Mientras
i =i + 1
Fin-Mientras
Fin
El algoritmo ins_grafo se encarga de crear el nodo en memoria que conformará la lista de adyacencia
para el vértice i, dentro del arreglo grafo.
123
q =p
p = *p.sig
Fin-Mientras
Si (q = A)
*grafo [i].sig = nuevo
Sino
*q.sig = nuevo
Fin-Si
Fin
El algoritmo Recorrer_grafo ejecuta la operación de Visita a cada nodo adjunto de todos los vértices de
un grafo.
i =1
Mientras (i<= nv)
p =grafo [i]
Visite_Vertice (p)
q =*grafo [i].sig
Mientras (q =A)
Visite_Vertice (q)
q =*q.sig
Fin-Mientras
i=i +1
Fin-Mientras
Fin
124
Figura 7.20 Matriz de adyacencia
para el grafo de la figura 7.18
7.2.2.1 Almacenamiento en memoria. Se tiene una matriz bidimensional denominada grafo. El orden
de la matriz es nv * nv, donde nv es el número de vértices en el grafo. Cada elemento matriz[i,j] tiene
un valor de 0 si el nodo ( vértice ) j es adjunto al nodo i.
Leer_grafo (grafo)
Inicio
nv =Leer_Numero_de_Vertices(grafo)
lniciar_grafo(grafo,nv)
i =1
Mientras (i <= nv)
j =Leer_adjunto(grafo[i])
Mientras (j )
matriz [i, j] =1
j =Leer_adjunto(grafo[j])
Fin-Mientras
i =i + 1
Fin-Mientras
Fin
Esta estructura es aplicable para los dígrafos. En la matriz de incidencia cada fila representa a cada uno
de los nodos del grafo, y las columnas los posibles arcos de dicho grafo; en la casilla M [i ,j ], aparecerá
un 1 cuando el nodo de la fila i es inicial, y un -1, cuando el nodo i es final. En la siguiente figura
aparece un dígrafo y su correspondiente matriz de incidencia:
125
7.2.3.1 Almacenamiento en memoria. Para este caso también se maneja una matriz bidimensional
denominada grafo, pero ahora el elemento grafo[i, j] será igual a 1 si el nodo i es inicial, y será igual a
-1 si es el nodo final. Tendrá un valor de cero si los dos nodos, el nodo i y el nodo j no están conectados
directamente.
Leer_grafo (grafo)
Inicio
j =Leer_nodo_de_salida(grafo[i])
Mientras (j ) Haga
grafo [i, j] = 1
j =Leer_nodo_de_salida(grafo[j])
Fin-Mientras
i =i + 1
Fin-Mientras
Fin
7.3 APLICACIONES.
Dentro de las aplicaciones clásicas y fundamentales se encuentra el estudio de redes. En este campo se
han desarrollado técnicas como son Cálculo del Flujo Máximo y Cálculo del Costo Mínimo; en ambos
casos se trata de optimización en redes.
• METODO DE CORTES
• METODO DE GRAFICO AUXILIAR
• METODO DE FORD - FULKERSON
126
7.3.1 Algoritmo De Dijkstra
Este algoritmo usado para determinar el costo mínimo, es el más implementado en ciertos dispositivos
de Hardware. Se encarga de determinar las rutas con el menor costo, desde un nodo origen hacia todos
los otros nodos de un grafo.
El algoritmo va pasando por diferentes estados. En el estado K, las rutas más cortas a los K nodos más
cercanos han sido determinadas y estos nodos están dentro de un grupo denominado M. En el estado K
+ 1, un nodo que no está en M y que tiene la ruta más corta desde el nodo origen es incluido en M.
Al final todos los nodos estaran en M, y sus rutas desde el origen habran sido determinadas.
El algoritmo tiene tres pasos; los pasos 2 y 3 son repetidos hasta que M = N. Es decir tales pasos son
repetidos hasta que las rutas finales han sido asignadas a todos los nodos de la red:
1. Inicializar:
M = {s}
// Al grupo de nodos M solo se incorpora el nodo origen.
Dn = dsn para n >s
// Los costos de las rutas iniciales hacia los nodos vecinos son simplemente los costos del enlace.
2. Encontrar el nodo vecino que no está en M y que tiene el enlace con menor costo desde el nodo s. El
nodo encontrado se incorpora a M.
Encontrar w M tal que Dw = min { Dj } para j M
Insertar w a M.
Cada iteración de los pasos 2 y 3 coloca un nuevo nodo en M y define la ruta con costo mínimo desde s
hasta ese nodo. Esa ruta solo pasa a través de nodos que están en M.
127
Figura 7.22 Dígrafo
La siguiente tabla muestra el resultado de aplicar el algoritmo sobre el grafo de la figura 7.22, usando s
=1 como nodo de origen.
128
Figura 7.23 Algoritmo de Dijkstra
129