Download árboles binarios

Document related concepts

Árbol binario wikipedia , lookup

Recorrido de árboles wikipedia , lookup

Árbol binario de búsqueda wikipedia , lookup

Treap wikipedia , lookup

Rotación de árboles wikipedia , lookup

Transcript
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
UNIDAD VI
ÁRBOLES BINARIOS
6.1. REPRESENTACIÓN DE ÁRBOLES BINARIOS
6.1.1. TERMINOLOGÍA DE ÁRBOLES BINARIOS
Las estructuras dinámicas lineales de datos( listas enlazadas, pilas, colas) tienen grandes ventajas
de flexibilidad sobre las representaciones contiguas, pero tienen un problema son listas secuenciales, es
decir, que por la forma en la que están dispuestas es necesario moverse un elemento a la vez debido a que
cada elemento tiene un siguiente elemento, esta linealidad es típica de cadenas, de elementos que
pertenecen a una sola dimensión: campos en un registro, entradas en una pila, entradas en una cola y
nodos en una lista enlazada simple. En esta parte se mostraran las estructuras de datos no lineales
conocidos como árboles, adicionalmente podremos decir que no es el único tipo de estructura no lineal
pues a parte de los árboles existen los grafos la cual tiene aplicaciones muy diversas en las ciencias.
6.1.2. IMPLEMENTACION DE UN ARBOL BINARIO
Un árbol binario es un conjunto finito de elementos que está vacío, o está dividido en tres
subconjuntos desarticulados. El primer subconjunto contiene un solo elemento llamado raíz del árbol. Los
otros dos son en sí mismos, árboles binarios, llamados subárboles izquierdo y derecho del árbol original.
Un subárbol izquierdo o derecho puede estar vacío. Cada elemento de un subárbol se llama nodo del
árbol.
En la siguiente figura se muestra un árbol binario. Este árbol consiste en nueve nodos y tiene ‘A’
como raíz. Su subárbol izquierdo tiene a ‘B’ como raíz y su subárbol derecho a ‘C’.
A
B
D
C
E
G
F
H
I
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
80
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
La siguiente figura muestra estructuras de árboles que no son binarios. De tal modo que, si ‘A’ es
la raíz de un árbol binario y ‘B’ es la raíz de su subárbol derecho o izquierdo entonces ‘A’ se llama el
padre de ‘B’, y ‘B’ el hijo izquierdo o derecho de ‘A’. Un nodo que no tiene hijos se llama hoja. El nivel
de un nodo en un árbol binario se define como sigue. La raíz del árbol tiene nivel 0 (cero) y el nivel de
cualquier otro nodo del árbol es uno más que el nivel de su padre. Un árbol binario de completo de
profundidad ‘D’ es el árbol estrictamente binario cuyas hojas están en el nivel ‘D’. En la siguiente figura
se muestra un árbol de profundidad 3.
A
B
C
D
G
E
F
H
I
A
B
C
D
F
E
G
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
81
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
OPERACIONES CON ÁRBOLES BINARIOS
Existen varias operaciones simples que pueden aplicarse a árboles binarios. Si p es un apuntador a
un nodo nd de un árbol binario, la función info(p) da como resultado los contenidos de nd. Las funciones
left(p), right(p), father(p), da como resultado el apuntador nulo sí nd no tiene hijo izquierdo, hijo derecho
padre o hermano. Como consecuencia las funciones isleft(p) e isright(p) dan como resultado verdadero
(true) sí nd es hijo izquierdo o derecho, respectivamente, de algún otro nodo del árbol, y falso (false) en
caso contrario.
q = father(p);
if(q == null)
return(false);
if(left (q) == p)
return(true);
return(false);
APLICACIONES DE ÁRBOLES BINARIOS
Un árbol binario es una estructura de datos útiles cuando en cada punto de un proceso hay que
tomar una decisión de doble opción. Como en la aplicación para encontrar duplicados:
/* Leer el primer número e insertarlo en un árbol binario de un solo nodo */
scanf (“%d”, &number);
tree = maketree (number);
while (hay número rezagados en la entrada)
{
scanf (“%d”, number);
p = q;
while (number != info(p) && q != NULL)
{
p = q;
if (number < info(p))
q = left(p);
else
q = right(p);
} /* Fin del while */
if (number == info(p))
printf (“%d %s \n”, number, “es un duplicado”);
/* Insertar number a la derecha o izquierda de p */
else if (number < info(p))
setleft(p, number);
else
setright(p, number);
} /* Fin del while */
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
82
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
Otra operación común es recorrer un árbol binario; esto e pasar a través del árbol, enumerando
cada uno de sus nodos una vez. En cualquier caso se habla de visitar cada nodo al enumerar este. El orden
en que se visitan los nodos es de una manera clara, del primero al último.
Para recorrer un árbol binario lleno de preorden, se ejecutan las siguientes tres operaciones:
1. Visitar la raíz.
2. Recorrer el subárbol izquierdo en preorden.
3. Recorrer el subárbol derecho en preorden.
Para visitar un árbol binario lleno de orden, se ejecutan las siguientes tres operaciones:
1. Recorrer el subárbol izquierdo en orden.
2. Visitar la raíz.
3. Recorrer el subárbol derecho en orden.
Para recorrer un árbol binario lleno en postorden, se ejecutan las siguientes tres
operaciones:
1. Recorrer el subárbol izquierdo en postorden.
2. Recorrer el subárbol derecho en postorden.
3. Visitar la raíz.
A
B
C
F
D
E
G
H
I
Preorden: ABDGCEHIF
En orden: DGBAHEICF
Postorden: GDBHIEFCA
E
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
83
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
E
E
E
E
E
E
E
E
E
E
E
Preorden: ABCEIFJDHLA
En orden: EICFJBGDKHLA
Postorden: IEJFCGKLHDBA
REPRESENTACIÓNES DE ÁRBOLES BINARIOS.
Al igual que en el caso de los nodos de una lista, los nodos de un árbol pueden implantarse como
un arreglo de elementos o como asignación de una variable dinámica. Cada nodo contiene los campos
info, left, y father. Los campos left, right y father apuntan a los nodos hijo derecho, hijo izquierdo y padre
respectivamente.
#define NUMNODES 500
struct nodetype
{
int info;
int left;
int right;
int father;
};
struct nodetype node [numnodes];
ELECCIÓN DE UNA REPRESENTACIÓN DE ÁRBOLES BINARIOS.
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
84
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
La representación ligada requiere de campos left, right y father (aunque ya se ha visto que uno o
dos de ellos pueden eliminase en situaciones específicas), pero permite un uso mucho más flexible del
conjunto de nodos. En la representación ligada, un nodo particular puede colocarse en cualquier
localización y en cualquier árbol, mientras que en la representación secuencial un nodo se puede usar sólo
si se necesita en una localización en un árbol específico. Además en la representación con nodos
dinámica, el número total de árboles y nodos está limitado exclusivamente por la cantidad de memoria
disponible. Así la representación ligada es preferible en la situación dinámica general de muchos árboles
en forma impredecible.
RECORRIDOS DE ÁRBOLES BINARIOS EN C.
Es posible implantar el recorrido de árboles binarios en C por medio de rutinas recursivas que
reflejan las definiciones del recorrido. Las tres rutinas en C: pretav, intrav y posttrav, imprimen los
contenidos de un árbol binario en preorden, orden y postorden. El parámetro de cada rutina es un
apuntador al nodo raíz de un árbol binario.
pretav(tree)
NODEPTR tree;
{
if (tree != NULL)
{
printf (“%d”, tree-info);
pretav (tree- left);
pretav (tree - right);
} /* fin del if */
} /* fin del pretav */
intrav (tree)
NODEPTR tree;
{
if (tree != NULL)
{
intrav (tree - left);
printf (“%d”, tree - info);
intrav (tree - right);
} /* fin del if */
} /* fin del intrav */
posttrav (tree)
NODEPTR tree;
{
if (tree != NULL)
{
posttrav (tree - left);
posttrav (tree - right);
printf (“%d”, tree - info);
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
85
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
} /* fin del if */
} /* fin del posttrav */
6.2. OPERACIONES SOBRE UN ARBOL BINARIO
6.2.1. CONSTRUYE UN ARBOL
6.2.2. RECORRIDO DE ARBOL BINARIO
6.2.2.1 PREORDEN
6.2.2.2 EN ORDEN
6.2.2.3 POSTORDEN
6.2.3. BÚSQUEDA DE UN NODO
6.2.4. INSERCIÓN DE UN NODO
6.2.5. ELIMINACIÓN DE UN NODO
Las listas enlazadas, las pilas y las colas son estructuras lineales de datos. Un árbol es una
estructura lineal y de dos dimensiones de datos, con propiedades especiales, Los nodos de los árboles
contienen dos o más enlaces, árboles cuyos nodos todos ellos contienen dos enlaces (ninguno, uno, o
ambos de los cuales pudiera ser NULL). El nodo raíz es el primer nodo de un árbol. Cada enlace en el
nodo raíz se refiere aun hijo. El hijo izquierdo s el primer nodo en el subárbol izquierdo y el hijo derecho
es el primer nodo en el subárbol izquierdo. Los hijos de un nodo se conocen como descendientes. Un
nodo sin hijos se conoce como un nodo de hoja. Los científicos de la computación normalmente dibujan
los árboles partiendo del nodo raíz hacia abajo – en forma exactamente opuesta a los árboles en la
naturaleza.
Crearemos un árbol binario especial conocido como un árbol de búsqueda binario (que no tiene
valores duplicados de nodos) tienen la característica que los valores en cualquier subárbol izquierdo son
menores que el valor en sus nodos padre, y los valores en cualquier subárbol derecho son mayores que el
valor en sus nodos padre. En la figura se ilustra un árbol de búsqueda binario con 12 valores. Note que la
forma del árbol de búsqueda binario que corresponde a un conjunto de datos puede variar, dependiendo
del orden en el cual los valores están insertos dentro del árbol.
47
25
11
77
43
69
93
El programa de la siguiente
figura,
crea un árbol de búsqueda
binario y lo68recorre de tres formas,
7
17
31
44
en orden, preorden y postorden. El programa genera 10 números aleatorios e inserta cada uno de ellos en
el árbol, a excepción de los valores duplicados, que son descartados.
Las funciones utilizadas, para crear un árbol de búsqueda binario y recorrer el árbol, son
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
86
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
recursivas. La función insertNode recibe como argumentos la dirección del árbol y un entero para
almacenarse en el árbol. En un árbol de búsqueda binario un nodo puede ser únicamente inserto como
nodo de hoja, Los pasos para insertar un nodo en un árbol de búsqueda binario, son como sigue:
1. Si treePtr es NULL, crea un nuevo nodo. Llame malloc, asigna la memoria asignada a treePtr,
asigna a (treePrt)->data el entero a almacenarse, asigna (*treePtr)->leftPtr y (*treePtr)->rightPtr
el valor NULL y devuelve o regresa el control al llamador (ya sea a main o a una llamada anterior
a inserNode).
2. Si el valor de treePtr no es NULL, y el valor a insertarse es menor que (treePtr)->data, se
llama a la función insertNode con la dirección(*treePtr)->leftPtr. De no ser así, se llama a la
función insertNode con la dirección de (*treePtr)->righptr. Se continúan los pasos recursivos
hasta que se encuentre un apuntador NULL, entonces se ejecutara el paso 1) para insertar el
nuevo nodo.
Las funciones inOrder, preOrder y postOrder cada una de ellas recibe una árbol (es decir, el
apuntador al nodo raíz del árbol) y recorren el árbol.
Los pasos para recorrido un recorrido inOrder son:
1. Recorrer el subárbol izquierdo inOrder.
2. Recorrer el valor en el nodo.
3. Recorrer el subárbol derecho inOrder.
El valor en un nodo no es procesado en tanto no sean procesados los valores de su subárbol
izquierdo. El recorrido inOrder del árbol es:
6 13 17 27 33 42 48
Nótese que el recorrido inOrder de un árbol de búsqueda binario imprime los valores de nodo en
valor ascendente. El proceso de crear un árbol de búsqueda binario, de hecho ordene los datos y por lo
tanto este proceso se llama la clasificación de árbol binario.
27
42
13
6
17
33
48
El árbol de búsqueda binario facilita la eliminación de duplicación. Conforme se crea el árbol
cualquier intento para insertar un valor duplicado será detectado porque en cada una de las comparaciones
un duplicado seguirá las mismas decisiones “ir a la izquierda” o “ir a la derecha” que utilizo el valor
original. Entonces, el duplicado eventualmente será comparado con un nodo que contenga el mismo valor.
Llegado a este punto el valor duplicado pudiera simplemente sé desertado.
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
87
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
También es rápido buscar en un árbol binario un valor que coincida con un valor clave. Si el valor
es denso, entonces cada nivel contendrá aproximadamente dos veces tantos elementos como el nivel
anterior. Por lo tanto un árbol de búsqueda binario con n elementos tendría un máximo de log2n
(logaritmo de base 2 de n niveles) y, por lo tanto, tendrían que efectuarse un máximo log2n de
comparaciones, ya sea para encontrar una coincidencia, o para determinar que no existe ninguna. Esto
significa, por ejemplo, que al buscar en un árbol de búsqueda binario de 1000 elementos(densamente
empacados), no es necesario llevar a cabo mas de 10 comparaciones porque 2n >1000. Al buscar en un
árbol de búsqueda binario 1000000 (densamente empacados), no es necesario llevar a cabo mas de 20
comparaciones, porque 2 a la 20 >1000000.
En los ejercicios se presentan algoritmos para otras operaciones de árboles de búsqueda binarios,
como sé borrar un elemento de un árbol binario, imprimir un árbol binario en un formato de árbol de dos
dimensiones, ejecutar un recorrido en orden de niveles de un árbol binario, imprimir un árbol binario en
un formato de árbol de dos dimensiones, y ejecutar un recorrido en orden de niveles de un árbol binario.
El recorrido de orden de niveles de un árbol binario pasa por los nodos de un árbol, se pasa por los nodos
de izquierda a derecha. Otros ejercicios de árboles binarios incluyen permitir que un árbol de búsqueda
binario contenga valores duplicados, insertar valores de cadenas de un árbol binario, y determinar cuantos
niveles incluidos en u árbol binario.
/* PROGRAMA de ARBOL */
/* DECLARACION DE LAS LIBRERIAS */
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
/* ESTRUCTURA AUTOREFERENCIADA */
struct treeNode
{
struct treeNode *leftPtr;
int data;
struct treeNode *rightPtr;
};
/* DEFINICION DE TIPOS */
typedef struct treeNode TREENODE;
typedef TREENODE *TREENODEPTR;
/* ENCABEZADO DE LAS FUNCIONES */
void insertNode(TREENODEPTR *, int);
void inOrder (TREENODEPTR);
void preOrder (TREENODEPTR);
void postOrder (TREENODEPTR);
/* FUNCIONES PRINCIPALES */
main()
{
int i, item;
TREENODEPTR rootPtr =NULL;
/* DECLARACION DE DATOS */
srand(time(NULL));
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
88
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
/* intentar a insertar 10 valores aleatorios entre 0 y 14 en el arbol*/
printf("Los numeros iniciales en el arbol son: \n");
for(i=1;i<=10;i++)
{
item = rand() % 15;
printf("%3d", item);
insertNode(&rootPtr, item);
}
printf("\n\n El preorden tranversal es : \n");
preOrder(rootPtr);
printf("\n\n El inorden tranversal es : \n");
inOrder (rootPtr);
printf("\n\n El postorden tranversal es : \n");
preOrder (rootPtr);
return 0;
}
/* Insertar nodo */
void insertNode(TREENODEPTR *treePtr, int value)
{
if (*treePtr == NULL)
{
*treePtr = (TREENODEPTR) malloc(sizeof(TREENODEPTR));
if (*treePtr != NULL)
{
(*treePtr)->data = value;
(*treePtr)->leftPtr = NULL;
(*treePtr)->rightPtr = NULL;
}
else
printf("%d no inserto. Memoria insuficiente. \n\n, info");
}
else
if(value < (*treePtr)->data)
insertNode (&((*treePtr) -> leftPtr), value);
else
if (value > (*treePtr)->data)
insertNode (&((*treePtr)->rightPtr), value);
else
printf("duplicado");
}
/* Retirar un nodo*/
void inOrder (TREENODEPTR treePtr)
{
if(treePtr != NULL)
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
89
ÁRBOLES
UNIDAD VI
____________________________________________________________________________________________________
{
inOrder(treePtr->leftPtr);
printf("%3d", treePtr->data);
inOrder (treePtr->rightPtr);
}
}
void preOrder(TREENODEPTR treePtr)
{
if(treePtr != NULL)
{
printf("%3d", treePtr->data);
preOrder(treePtr->leftPtr);
preOrder (treePtr->rightPtr);
}
}
void postOrder(TREENODEPTR treePtr)
{
if(treePtr != NULL)
{
postOrder(treePtr->leftPtr);
postOrder (treePtr->rightPtr);
printf("%3d", treePtr->data);
}
}
____________________________________________________________________________________________________
ESTRUCTURA DE DATOS
ING. OSORNIO
90