cv.cm

Programación · Estructuras de datos

Árbol binario de búsqueda explicado: cómo funciona, código y complejidad

6 min de lectura

Respuesta corta

Un árbol binario de búsqueda (BST) es un árbol binario en el que el subárbol izquierdo de cada nodo solo contiene claves menores y el derecho solo claves mayores. Gracias a esta regla, buscar, insertar y eliminar siguen un único camino desde la raíz y cuestan O(h), donde h es la altura del árbol: O(log n) si el árbol está balanceado, pero O(n) si degenera en una cadena. Un recorrido inorder visita las claves en orden ascendente.

Qué problema resuelve un árbol binario de búsqueda

Un arreglo ordenado permite búsqueda binaria rápida, pero insertar un elemento nuevo desplaza todo lo que sigue. Una tabla hash busca e inserta rápido, pero no mantiene orden, así que no puede responder "¿cuál es la menor clave mayor que 42?" ni "lista todas las claves entre 10 y 20". Un árbol binario de búsqueda mantiene las claves ordenadas y aun así permite insertar y eliminar con rapidez. Es la base de los mapas y conjuntos ordenados como TreeMap de Java y std::map de C++, que usan variantes autobalanceadas.

La regla en palabras sencillas

Cada nodo tiene una clave, un hijo izquierdo opcional y un hijo derecho opcional. Para cada nodo: todas las claves de su subárbol izquierdo son menores y todas las de su subárbol derecho son mayores. Para buscar una clave, empieza en la raíz y compara: ve a la izquierda si la clave es menor, a la derecha si es mayor, y detente si es igual o si te sales del árbol.

Ejemplo resuelto: construir y buscar en un árbol

Inserta 8, 3, 10, 1, 6, 14, 4 en este orden. Cada clave nueva baja desde la raíz y se convierte en hoja donde se sale del árbol.

InserciónCamino recorridoQueda como
8árbol vacíoraíz
38: menor, ir a la izquierdahijo izquierdo de 8
108: mayor, ir a la derechahijo derecho de 8
18 izquierda, 3 izquierdahijo izquierdo de 3
68 izquierda, 3 derechahijo derecho de 3
148 derecha, 10 derechahijo derecho de 10
48 izquierda, 3 derecha, 6 izquierdahijo izquierdo de 6

Buscar el 4 compara con 8, 3, 6 y luego 4: cuatro comparaciones en un árbol de siete claves. Buscar el 7 compara con 8, 3, 6 y luego no encuentra hijo derecho del 6, así que el 7 no está.

Implementación en Python

Insertar y buscar son bucles cortos. Eliminar tiene tres casos: el nodo no tiene hijos (se quita), tiene un hijo (se reemplaza por él) o tiene dos hijos (se copia su sucesor inorder, la clave menor del subárbol derecho, y luego se elimina ese sucesor).

class Node:
    def __init__(self, key):
        self.key, self.left, self.right = key, None, None
def insert(root, key):
    if root is None:
        return Node(key)
    if key < root.key:
        root.left = insert(root.left, key)
    elif key > root.key:
        root.right = insert(root.right, key)
    return root                      # duplicates are ignored
def contains(root, key):
    while root and root.key != key:
        root = root.left if key < root.key else root.right
    return root is not None
def delete(root, key):
    if root is None:
        return None
    if key < root.key:
        root.left = delete(root.left, key)
    elif key > root.key:
        root.right = delete(root.right, key)
    else:
        if root.left is None:
            return root.right
        if root.right is None:
            return root.left
        succ = root.right
        while succ.left:
            succ = succ.left
        root.key = succ.key
        root.right = delete(root.right, succ.key)
    return root
def inorder(root):
    return inorder(root.left) + [root.key] + inorder(root.right) if root else []
root = None
for k in [8, 3, 10, 1, 6, 14, 4]:
    root = insert(root, k)
print(inorder(root))                 # [1, 3, 4, 6, 8, 10, 14]
print(contains(root, 4), contains(root, 7))  # True False
root = delete(root, 3)               # node with two children
print(inorder(root), root.left.key)  # [1, 4, 6, 8, 10, 14] 4

Complejidad temporal y espacial

OperaciónÁrbol balanceadoPeor caso (cadena)Motivo
BúsquedaO(log n)O(n)una comparación por nivel
InserciónO(log n)O(n)bajar hasta una posición de hoja
EliminaciónO(log n)O(n)encontrar el nodo y quizá su sucesor
Mín / máxO(log n)O(n)seguir los enlaces izquierdos (o derechos) hasta el final
Recorrido inorderO(n)O(n)cada nodo una vez
EspacioO(n)O(n)un nodo por clave; la recursión usa O(h) de pila

El peor caso es real, no teórico: insertar claves ya ordenadas (1, 2, 3, ...) en un BST simple deja a cada nodo solo con hijo derecho, y el árbol se convierte en una lista enlazada. Los órdenes de inserción aleatorios dan una altura esperada de O(log n).

Variantes balanceadas y errores comunes

Los árboles autobalanceados mantienen la altura en O(log n) reestructurándose con rotaciones tras insertar y eliminar.

VarianteIdea de balanceoDónde se usa
Árbol AVLlas alturas de los dos subárboles difieren como máximo en 1cargas con muchas búsquedas
Árbol rojo-negroreglas de color limitan el camino más largo a el doble del más cortoTreeMap de Java, implementaciones típicas de std::map de C++, el kernel de Linux
Árbol B / B+muchas claves por nodo, todas las hojas a la misma profundidadbases de datos y sistemas de archivos
Treap, splay treeprioridades aleatorias, o mover las claves accedidas a la raízárboles balanceados más simples, cachés
  • Validar un BST comprobando solo padre e hijo. Pasa en su lugar un rango válido (low, high) hacia abajo.
  • Olvidar devolver la raíz (posiblemente nueva) en la inserción y eliminación recursivas, de modo que los cambios se pierden.
  • Recursión profunda en un árbol degenerado: el límite de recursión por defecto de Python es de unos 1000, así que una cadena de 5000 nodos hace fallar el código recursivo. Usa bucles o mantén el árbol balanceado.
  • Política de duplicados indefinida: decide si los duplicados se ignoran, se cuentan o van siempre hacia un lado.

Cuándo usar un BST y preguntas típicas de entrevista

Usa un BST balanceado cuando necesites datos ordenados con inserciones y eliminaciones frecuentes: consultas por rango, búsquedas floor y ceiling, estadísticos de rango u orden, o iterar en orden. Usa una tabla hash cuando solo necesites buscar una clave exacta. En Python, la biblioteca estándar no trae un mapa en árbol; las opciones comunes son una lista ordenada con el módulo bisect para datos pequeños o casi estáticos, o el paquete de terceros sortedcontainers.

Preguntas típicas de entrevista: validar un BST, encontrar el k-ésimo elemento menor (recorrido inorder), ancestro común más bajo, convertir un arreglo ordenado en un BST balanceado (elige el medio como raíz) y hallar el sucesor inorder de un nodo.

Preguntas frecuentes

¿Cuál es la diferencia entre un árbol binario y un árbol binario de búsqueda?

Un árbol binario solo limita cada nodo a dos hijos. Un árbol binario de búsqueda añade la regla de orden: claves menores a la izquierda y mayores a la derecha, en cada subárbol.

¿Por qué la búsqueda en un BST es O(log n)?

Cada comparación baja un nivel y descarta un subárbol. Un árbol balanceado con n nodos tiene unos log2(n) niveles, así que la búsqueda necesita aproximadamente ese número de comparaciones.

¿Qué pasa si inserto datos ordenados en un BST?

Sin balanceo, cada clave nueva pasa a ser hijo derecho de la anterior, la altura se vuelve n y toda operación pasa a ser O(n). Los árboles autobalanceados como AVL y rojo-negro lo evitan.

¿Cómo se obtienen las claves de un BST en orden?

Haz un recorrido inorder: visita el subárbol izquierdo, luego el nodo y luego el subárbol derecho. Toma tiempo O(n).

¿Python tiene un árbol binario de búsqueda integrado?

No. Python tiene dict y set (tablas hash) y el módulo bisect para listas ordenadas. Los contenedores de árbol ordenado vienen de paquetes de terceros.

Todos los tutoriales de programación