Laboratorio de Estructuras de Datos II

Anuncio
Laboratorio de Estructuras de Datos II
Segundo examen parcial
Fecha límite de entrega: Jueves 19 de noviembre de 2009, 3:59:59 p.m.
Los árboles B son estructuras de datos jerárquicas que se utilizan para almacenar y
manipular datos ordenados de forma muy eficiente, ya que por su estructura y sus
propiedades las inserciones y las eliminaciones se realizan en un tiempo logarítmico
amortizado. Por esta razón, se utilizan para crear índices dentro de las bases de datos
relacionales, para agilizar la búsqueda de registros.
Un nodo de un árbol B de orden n puede tener (n) hijos, por lo cual contendrá n-1 datos.
Por ejemplo, un árbol B de orden 4 tiene la siguiente estructura:
5
2
3
4
7
8
9
15
12
14
25
35
En esta estructura (para el ejemplo un árbol de enteros, de orden 4), se debe garantizar
que:

Los datos dentro de cada nodo se encuentran ordenados de forma ascendente.

Todos los datos de los hijos almacenados a la izquierda de cualquier dato deben ser
menores que él y todos los datos de los hijos almacenados a la derecha de
cualquier dato deben ser mayores que él.

Todas las hojas del árbol se encuentran en el mismo nivel
Inserción en un árbol B
El proceso de insertar un valor en un árbol B se realiza siempre en los nodos hoja, de la
siguiente forma:

Si el árbol está vacío, se debe crear un nuevo nodo B e insertar el nuevo valor. Este
nodo se convierte en la raíz del árbol.

Si el árbol no está vacío, primero se deberá verificar que el nuevo dato no exista
dentro del árbol. Para ello se deberá realizar una búsqueda empezando desde la
raíz. Si el nuevo dato no existe, el proceso de búsqueda deberá hallar el nodo hoja
(B) en el cual se va a insertar el nuevo nodo.

Una vez encontrado el nodo hoja (B) en el cual se almacenará el nuevo dato se
pueden presentar dos casos:
La hoja (B) tiene espacio para almacenar el nuevo dato. Se debe insertar el
dato dentro del nodo B y terminar.
o La hoja (B) no tiene espacio: En este caso la hoja se deberá dividir en dos
nodos B. Se deberán disponer los valores (que estaban almacenados en la
hoja más el nuevo valor) de forma ascendente y se tomará el valor de la
mitad (n / 2). Este valor se deberá llevar al nodo B padre (si no existe, se
deberá crear uno). El hijo izquierdo del dato que se llevó al padre deberá
contener los valores menores que él en la hoja original, y el hijo derecho
deberá contener los datos mayores que él en la hoja original.
Si al llevar un dato al nodo padre no existe espacio para almacenarlo, se deberá
dividir el nodo en dos (como en el paso descrito anteriormente).
o

Implementación de un nodo de árbol B
El nodo B se deberá implementar como una lista de nodos binarios, cada uno de los
cuales tiene un apuntador izquierdo y derecho a sus nodos B hijos. Tenga en cuenta que
los nodos binarios dentro del nodo B comparten el apuntador derecho e izquierdo con sus
vecinos. La siguiente gráfica ilustra cómo se debe implementar la estructura de datos para
el nodo B (y el nodo binario dentro del nodo B).
padre
izq
der
Problema
Se deberá implementar un programa que lea comandos desde la entrada estándar para
insertar y buscar personas (cada una con codigo, primer nombre, segundo nombre, primer
apellido y segundo apellido) en un árbol B que se encuentra inicialmente vacío. El
programa deberá terminar cuando encuentre una línea en el archivo con la palabra “salir”.
El criterio de ordenamiento de las personas en el árbol B es su codigo.
Cada vez que se busque una persona dentro del árbol se deberá imprimir el número de
pasos que se realizaron para encontrarla, comparados con el número de pasos
necesarios para buscar la persona en un árbol AVL.
Los comandos que se deben implementar son:
orden n
cargar archivo
buscar #codigo
Define el orden del árbol B: El máximo número de hijos que puede
tener cualquier nodo dentro del árbol.
Cargar los datos del árbol B a partir de un archivo llamado archivo
Busca la persona cuyo codigo es #codigo dentro del árbol B e
salir
imprime el número de pasos que tuvo que realizar para encontrar
el valor. En caso que no se encuentre la persona en el árbol
también deberá imprimir el número de pasos que realizó. El
número de pasos debe incluir la búsqueda dentro de cada nodo B.
Para cada caso se deberá imprimir también el número de pasos
que se tuvo que realizar para encontrar el valor en un árbol AVL.
Termina la ejecución.
El formato del archivo que se debe leer con el comando cargar es el siguiente:
#El archivo puede contener comentarios o líneas en blanco que deben ser ignorados
#Cada línea describe a una persona. Los campos para cada persona están separados
#por espacios, y son los siguientes:
#codigo primer_nombre segundo_nombre primer_apellido segundo_apellido
#Los únicos campos obligatorios para cada persona son el codigo, el
#primer nombre y el primer apellido. Los campos faltantes se representan con “.”
13500710 juan . diaz .
10200340 pedro pablo perez .
Salida del programa
El programa sólo deberá imprimir cuando reciba un comando “buscar”. Por ejemplo, para
el comando:
buscar 13500710
El programa deberá imprimir si la persona existe o no dentro del árbol, el número de
comparaciones que se realizaron en el árbol B, y el número de comparaciones que se
tendrían que realizar en el árbol AVL.
Anexo: Algoritmos sugeridos para la implementación de los Árboles B
/* Insertar . Algoritmo para insertar un elemento en un árbol B.
Parámetros:
n: Referencia a la raíz del árbol (nodo b)
dato: Dato a insertar en el árbol */
insertar(a, dato)
baux = a /* Apuntador a la raíz del árbol (nodo b) */
ant = NULL /* Apuntador a la lista enlazada dentro del nodo b*/
nuevobin = crear_nodobin(dato)
padre = NULL
mientras baux != NULL
/* Sacar el inicio de la lista de datos del nodo b*/
ant = NULL
tmp = head(baux->datos)
mientras tmp != NULL and tmp->dato < dato
ant = tmp
tmp = next(tmp) //Siguiente de la lista!
fin_mientras
padre = baux //Almacenar el apuntador al padre
si tmp != NULL /* Se puede encontrar en este nodo b */
si dato == tmp->dato /* Existe en este nodo b?*/
retornar //Ya existe el dato!
fin_si
baux = tmp->izq //De lo contrario ir por la izquierda
sino /* Se termino la lista de datos, bajar al hijo derecho */
baux = ant->der
fin_si
fin_mientras
insertar_nodobin(padre, nuevobin)
fin_algoritmo
/* Insertar_nodobin . Algoritmo para insertar un nodo binario en un nodo B.
Parámetros:
a: Referencia al nodo B en el cual se debe insertar el nodo binario
nuevo: Nodo binario a insertar dentro del nodo B */
insertar_nodobin (a, nuevo)
si a == NULL //Nueva raiz!
raiz = crear_nodob()
a = raiz
//Se deben actualizar los padres de los nodos B izquierdo y derecho
//del nuevo nodo binario
si nuevo->izq != NULL //El nodo binario a insertar trae nodo B izquierdo?
nuevo->izq->padre = a
fin_si
si nuevo->der != NULL //el nodo binario a insertar trae nodo B derecho?
nuevo->der->padre = a
fin_si
fin_si
ant = NULL
tmp = head(a->datos)
//Buscar el sitio en el cual se debe insertar el nodo dentro del nodo b
mientras tmp != NULL && tmp->dato < nuevo->dato
ant = tmp
tmp = next(tmp)
fin_mientras
anterior = NULL
siguiente = NULL
//ant apunta al nodo anterior, tmp apunta al nodo siguiente
//Insertar el nodo después de ant. Si ant es nulo, se inserta al inicio de
//la lista
insert_after(a->datos, nuevo)
si ant != NULL
anterior = ant
fin_si
si tmp != NULL
siguiente = tmp
fin_si
//Actualizar la referencia derecha del nodo binario anterior
si anterior != NULL
anterior->der = nuevo->izq;
fin_si
//Actualizar la referencia izquierda del nodo binario siguiente
si siguiente != NULL
siguiente->izq = nuevo->der
fin_si
balancear(a)
fin_algoritmo
/* Balancear. Algoritmo para balancear un nodo B
Parametros:
a: Referencia al nodo B a balancear
*/
balancear(a)
aux = a
//Mientras el nodo este desbalanceado, balancear
mientras aux != NULL y aux->datos->count == ORDEN
ant=NULL
it = head(aux->datos)
//Hallar el nodo binario de la mitad dentro del nodo B
para i desde 0 hasta ORDEN / 2
ant=it
it=next(it);
fin_para
//it queda apuntando al nodo de la lista que se debe subir
//Sacar la referencia al nodo binario que se debe subir al padre
n = it
//Crear el nuevo nodo B que contiene lo datos n/2 + 1 ... n del nodo B actual
nuevob = crear_nodob()
nuevob->padre = aux->padre //El padre de nuevob es el mismo que aux
//El nodo binario que se sube al padre tiene como
//hijo izquierdo al nodo B original y como
//hijo derecho el nuevo nodo B
n->izq = aux
n->der = nuevob
//Avanzar al siguiente nodo binario:
//Estos nodos binarios se deben insertar en el nuevo nodob
it = next(it)
mientras it!=NULL
tmp = it
//Actualizar el padre de los nodos B hijos del nuevo nodo B
si tmp->izq != NULL
tmp->izq->padre = nuevob
fin_si
si it->next == NULL) //Ultimo nodo?
//actualizar la referencia derecha
si tmp->der != NULL
tmp->der->padre = nuevob
fin_si
fin_si
//Insertar el nodo binario en el nuevo nodo B
insertar_nodobin(nuevob, tmp)
//avanzar al siguiente nodo binario
it=next(it)
fin_mientras
//Ahora borrar los nodos sobrantes del nodo B original
mientras tail(aux->datos) != ant
pop_back(aux->datos)
fin_mientras
//Ahora llevar el nodo binario al nodo B padre!
insertar_nodobin(aux->padre, n)
//Iterar con el padre, para verificar si quedo balanceado
aux = aux->padre
fin_mientras
fin_algoritmo
/* Buscar . Algoritmo para buscar un elemento en un arbol B.
Parametros:
a : Referencia a la raiz del arbol (nodo b)
dato: Dato a buscar en el arbol */
buscar(a, dato)
baux = a /* Apuntador a la raiz del arbol (nodo b) */
ant = NULL /* Apuntador a la lista enlazada dentro del nodo b*/
encontrado=FALSE
mientras baux != NULL and encontrado == FALSE
/* Sacar el inicio de la lista de datos del nodo b*/
ant = NULL
tmp = head(baux->datos)
/* Comparar el dato con el primero de la lista */
si dato == tmp->dato
encontrado=TRUE
sino si dato < tmp->dato /* Bajar al hijo izquierdo */
baux = tmp->izq
sino /* Recorrer la lista de datos */
ant = NULL
mientras tmp != NULL and tmp->dato < dato
ant = tmp
tmp = next(tmp) //Siguiente de la lista!
fin_mientras
si tmp != NULL /* Se puede encontrar en este nodo b */
si dato == tmp->dato /* Existe en este nodo b?*/
encontrado=TRUE
sino
baux = tmp->izq
fin_si
sino /* Se termino la lista de datos, bajar al hijo derecho */
si dato > ant->dato
baux = ant->der
fin_si
fin_si
fin_si
fin_mientras
retornar encontrado
fin_algoritmo
Descargar