Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Binary Search Tree
Binary Search Tree
Agustín J. González
ELO-320: Estructura de Datos y Algoritmos
1
Introducción
Los árboles de búsqueda son estructuras de datos que soportan
las siguientes operaciones de conjuntos dinámicos:
Search -Búsqueda-, Minimum, Maximum, Predecessor, Successor,
Insert, y Delete.
Los árboles de búsqueda se pueden utilizar como diccionarios y
como colas de prioridad.
Estas operaciones toman un tiempo proporcional a la altura del
árbol.
Para un árbol completo binario esto es (lg n) en el peor caso; sin
embargo, si el árbol es una cadena lineal de n nodos, las mismas
operaciones toman (n) en el peor caso.
Para árboles creados aleatoriamente, la altura es O(lg n), con lo
cual los tiempos son (lg n).
Hay varios esquemas para mejorar el peor caso de los árboles de
búsqueda. Dos de ellos son los árboles 2-3 y los árboles rojo-
negro. 2
Propiedad de un árbol búsqueda binaria
Sea x un nodo en un árbol de x
búsqueda binaria. Si y es un nodo
del sub-árbol izquierdo de x, y z
entonces se cumple: (clave de y)
(clave de x). Si z es un nodo del y z
sub-árbol dercho de x, entonces la x x
se cumple (clave de x) (clave de
z). 2
5 3
Por ejemplo, dos árboles de
búsqueda binaria son:
3 7 7
2 8 5 8
5
La propiedad de árbol nos permite 5
recorrer o imprimir sus nodos en
el orden de sus claves haciendo
uso de un simple algoritmo
recursivo. 3
Recorrido Inorder de un árbol búsqueda
binaria
Suponiendo una estructura como la vista antes para árboles binarios, tenemos:
typedef struct arbol_tag {
struct arbol_tag * p;
struct arbol_tag * left;
struct arbol_tab * right;
elementType element;
} TREE_NODE;
void Inorder_Tree_Walk( TREE_NODE * x) {
if (x != NULL) {
Inorder_Tree_Walk(x->left);
Print(x->element); /* podría ser procesar elemento*/
Inorder_Tree_Walk(x->right);
}
}
Este algoritmo toma tiempo (n) porque el procedimiento es llamado exactamente
dos veces por cada nodo.
Análogamente se definen recorridos preorder y postorder del árbol. El único cambio
es el lugar de la instrucción de procesamiento del nodo. En preorder, el nodo se
procesa primero y en postorder se procesa después.
4
Recorrido Pre y port-order de un árbol
búsqueda binaria
void Preorder_Tree_Walk( TREE_NODE * x) {
if (x != NULL) {
Print(x->element); /* podría ser procesar elemento*/
Preorder_Tree_Walk(x->left);
Preorder_Tree_Walk(x->right);
}
}
void Postorder_Tree_Walk( TREE_NODE * x) {
if (x != NULL) {
Postorder_Tree_Walk(x->left);
Postorder_Tree_Walk(x->right);
Print(x->element); /* podría ser procesar elemento*/
}
}
El recorrido del árbol con los algoritmos previos daría: 5
Preorder: 5, 3, 2, 5, 7, 8 3 7
Inorder: 2, 3, 5, 5, 7, 8
Postorder: 2, 5, 3, 8, 7, 5 2 5 8 5
Otras operaciones en un árbol de
búsqueda binaria
Búsqueda de una clave determinada:
TREE_NODE * Tree_Search( TREE_NODE * x, elementType k) {
if (x == NULL) return x;
else if (x->element == k ) /* Ojo: esta comparación podría ser
una función*/
return x;
else if (k < x->element)
return Tree_Search( x->left, k);
else
return Tree_Search( x->right, k);
}
El tiempo de este algoritmo es O(h) donde h es la altura del árbol.
Este procedimiento se puede “desenrollar” para eliminar el tiempo de
múltiples llamados a la misma función. Ver -->
6
Otras operaciones en un árbol de
búsqueda binaria
Búsqueda de una clave determinada:
7
Máximo y Mínimo en un árbol de
búsqueda binaria
TREE_NODE * Tree_Maximum( TREE_NODE * x) {
if (x == NULL) return x;
while (x->right != NULL )
x = x->right;
return x;
}
9
Sucesor y Antecesor en un árbol de
búsqueda binaria
TREE_NODE * Tree_Predecessor( TREE_NODE * x) {
TREE_NODE * y;
if (x == NULL) return x;
if (x->left != NULL)
return Tree_Maximum(x->left);
y = x->p;
while ( y != NULL )
if (x == y->left) {
x = y;
y = y->p;
}
else break;
return y;
}
10
Inserción en un árbol de búsqueda binaria
Suponemos inicialmente que z->left = z->right = NULL.
Void Tree_Insert( TREE_NODE ** T, TREE_NODE * z) {
TREE_NODE *y, *x;
y=NULL;
x = *T;
while (x != NULL) { /* buscamos quien debe ser su padre */
y = x;
if ( z->element < x->element)
x = x->left;
else
x= x->right;
}
z->p = y;
if (y == NULL) /* se trata del primer nodo */
*T = z;
else if (z->element < y->element)
y->left = z;
else
y->right = z;
}
Como el procedimiento de búsqueda, este algoritmo toma un tiempo O(h), h es la
altura del árbol. 11
Eliminación en un árbol de búsqueda
binaria
Como entrada disponemos de z,
un puntero al nodo a remover. 1)
Hay tres casos a considerar: 15 15
1.- Que *z sea un nodo hoja. 5 16 5 16
En este caso se elimina 3 12 20 3 12 20
fácilmente. 10 13
z
18 23 10 18 23
2.- Que *z sea un nodo sin 6 6
hijo izquierdo o derecho. En 7 7
este caso, su único sub-árbol 2)
sube para toma el lugar de 15 15
z
*z. 5 16 5 20
3 3
3.-*z posee dos sub-árboles. 12 20 12 18 23
En este caso, su sucesor no 10 13 18 23 10 13
posee hijo izquierdo, luego 6 6
éste puede ser movido desde 7 7
su posición a la de *z. 12
Eliminación tercer caso
Caso 3)
z 15 15
z
5 16 5 16
3 12 20 3 12 20
10 13 18 23 10 13 18 23
6 6
7 7
z 15 15
5 16 6 16
3 6 12 20 3 12 20
10 13 18 23 10 13 18 23
7 7
13
Eliminación: Algoritmo
TREE_NODE * Tree-Delete(TREE_NODE **T, TREE_NODE * z) {
TREE:_NODE * x;
if (z->left == NULL || z->right == NULL)
y = z; /* caso 1 y 2 */
else
y = Tree_Successor(z); /* caso 3 */
/* hasta aquí y es un nodo con menos de dos hijos y debe ser extraído del árbol*/
if (y->left != NULL)
x = y->left;
else
x = y->right;
if (x != NULL) x->p = y->p;
if (y->p == NULL) /* estoy eliminado el último nodo */
*T = x;
else if (y == y->p->left) /* y es hijo izquierdo */
y->p->left = x;
else
y->p->right = x;
if (y !=z) /* caso 3 */
z->element = y->element;
return y;
} 14
Ejercicio:
Proponga un algoritmo codificado en C o pseudo lenguaje que
reciba como entrada un puntero a la raíz de un árbol binario y
retorne su altura. (Ayuda: observe que la altura de un nodo es uno
más que la altura mayor de sus hijos)
Sea la siguiente estructura para cada nodo del árbol:
typedef struct nodo_arbol {
struct nodo_arbol * p; /* puntero al padre */
struct nodo_arbol * left; /* hijo izquierdo */
struct nodo_arbol * right; /* hijo derecho*/
ELEMENTO elemento;
} NODO_ARBOL;
int Altura (NODO_ARBOL * T) {
int le, ri;
if (T==NULL) return -1; /* en realidad no está definida la
altura en este caso (por observación de Manuel Jander 2003)*/
le = Altura(T->left);
ri = Altura(T->right);
if (le > ri) return(le+1);
else return (ri+1);
15
}
Divertimento
Antes de que pasara lo que pasara (lo que todos
sabemos que pasó), los hijos de Adán y Eva estaban
muy unidos.
Si Abel iba a la fiesta, Set también iba. Si Set no iba a
la fiesta, Caín tampoco. Al menos uno de los tres fue a
la fiesta.
16