Montículo Binario

¿Qué representa este TAD?

Un montículo mínimo permite consultar rápidamente el menor valor en la raíz. Se guarda en un arreglo y también se dibuja como árbol; no exige que todo el arreglo esté ordenado.

Prueba este ejemplo

En un Montículo vacío carga 30, 10, 20. Ver raíz debe devolver 10. Extraer raíz retira ese mínimo; después la raíz será 20.

Qué observar · tres pasos

  1. Relaciona cada posición del arreglo con su nodo en el árbol.
  2. Al insertar o extraer, sigue los intercambios entre padres e hijos.
  3. Comprueba que cada padre es menor o igual que sus hijos al terminar. Ver arreglo consulta el orden interno, no un ordenamiento completo.

Ir a la visualización e ingresar estos datos

De la idea al resultado

  1. Prepara una operación y predice qué cambiará.
  2. Activa Paso a paso antes de ejecutar si quieres ver la traza; sin ese modo obtienes el resultado final.
  3. Relaciona el dibujo con la línea C resaltada: acaba de ejecutarse. Una condición puede no cambiar ningún nodo.
  4. Lee las pistas de Comprender y las pestañas de Resultados.

Paso anterior / Siguiente paso revisan la misma llamada. Pausar no deshace; cerrar Paso a paso muestra su final sin ejecutarla otra vez. Restablecer TAD (o Reiniciar, según el módulo) borra estructura e historial, no sólo el cursor.

Usa −, +, 100%, Encajar vista y Centrar inicio para acomodar el gráfico. El zoom y el desplazamiento se conservan durante los pasos; la memoria auxiliar es opcional.

Agregar varios valores en esta estructura

Disponible en Pila, Cola, Lista enlazada, Lista circular, ABB, AVL, Rojo-Negro y Montículo binario. En las listas agrega al final. No está disponible para Grafos, Hash, Cola de prioridad ni Sublista; Ordenamiento conserva su propia entrada de arreglo.

  1. Abre Agregar varios en Preparar. Escribe hasta 50 enteros separados por comas, por ejemplo 8, -3, 12. Se permiten espacios y signos; no decimales, posiciones vacías ni una coma final. Rango: −2147483648 a 2147483647.
  2. Revisa cantidad y orden. Se valida la lista completa antes de empezar. ABB, AVL y Rojo-Negro no aceptan repetidos, ni dentro del lote ni respecto al árbol actual. Pila, Cola, listas y Montículo sí los admiten.
  3. Elige Cargar valores para añadirlos en orden con resultado final, o Agregar paso a paso para estudiar cada inserción.
  4. En paso a paso, usa los controles del lote. Reproducir traza sólo reproduce el valor actual; Pausar detiene la reproducción. Al final, pulsa Siguiente valor para aceptar la siguiente inserción, o Finalizar lote al terminar la última.

Al terminar o detener un lote, edita la lista antes de iniciar otro. La vista previa conserva el orden solicitado; el resumen describe ese lote, no sustituye el estado actual tras otra operación o Restablecer TAD.

Detener lote conserva las operaciones ya aceptadas, incluida la actual, y descarta los valores pendientes. No es «deshacer todo». Si falla una operación durante el proceso, las anteriores no se revierten. Si la respuesta es incierta, usa Recargar estado e historial y comprueba qué se guardó antes de volver a cargar valores.

Palabras básicas para leer el dibujo
TAD
Tipo abstracto de dato: los datos y las operaciones permitidas sobre ellos.
Nodo
Elemento que guarda un valor y enlaces a otros elementos.
Puntero o enlace
Referencia que permite llegar a otro nodo. NULL indica que no apunta a un nodo.
Raíz, padre e hijo
La raíz inicia el árbol; un padre se conecta con sus hijos.
Tope, frente y atrás
Tope (TOP) en Pila; frente (FRONT) y atrás (BACK) en Cola son referencias a sus extremos.

Primera práctica de 5 minutos · Copiar, ampliar y exportar · Resolver dudas y errores · Atajos y referencia

Profundizar: invariantes, memoria y complejidad

Guía de aprendizaje

Objetivo: Predecir ascenso, descenso e intercambio mediante índices.

Estrategia: Mantener forma completa en arreglo y reparar prioridad padre-hijos.

Invariante: A[parent(i)] ≤ A[i] para todo i > 0.

Memoria C: El arreglo representa niveles; no usa enlaces de búsqueda.

Para leer el costo: n es la cantidad de nodos y h la altura. O(...) describe crecimiento del trabajo, no tiempo de reproducción.

Complejidad: Raíz O(1), reparar insertar/extraer O(log n); crecer con realloc puede copiar O(n). Ver arreglo copia O(n), y limpiar destruye/reinicializa el buffer.

Errores frecuentes

  • Tratar el heap como ABB.
  • Esperar que el arreglo esté totalmente ordenado.
Glosario jerárquico
Raíz
Nodo sin padre.
Hoja
Nodo sin hijos.
Altura
En ABB, AVL y el auxiliar de Rojo-Negro se cuentan niveles: árbol vacío 0, hoja 1. Revisa la convención de la función que estudias.
Profundidad
Distancia desde la raíz.
FE
Altura del subárbol derecho menos altura del izquierdo.
Rotación
Cambio local de enlaces que conserva el orden.
Recoloreo
Cambio de colores para reparar reglas rojo-negro.
Black-height
Cantidad de nodos negros por camino hasta NIL.
Heapify
Proceso de restaurar la propiedad de heap.

Teclado y accesibilidad

En la visualización, usa → para avanzar un paso, ← para retroceder, Inicio para volver al comienzo, Fin para ir al último paso y Espacio para pausar la reproducción. Los estados y explicaciones también se presentan con texto, no solo con color.

Alcance de esta pantalla

Referencia de operaciones y datos de entrada

Operaciones de la aplicación y datos

Ver arreglo a_lista
Sin datos adicionales.
Extraer raíz extraer_raiz
Sin datos adicionales.
Insertar insertar
Valor (entero)
Limpiar limpiar
Sin datos adicionales.
Ver raíz raiz
Sin datos adicionales.

Los métodos C siguientes pueden incluir helpers, utilidades sin botón propio y llamadores equivalentes. Un bloque con varias funciones no cambia el nombre de la operación de la aplicación. Las vistas de memoria usan identidades simbólicas; tras free, sus registros históricos no son punteros utilizables.

Ejecutar operación crea una llamada; Anterior/Siguiente recorren la misma traza. Cerrar y reactivar Paso a paso no vuelven a ejecutar. Las consultas completadas pueden conservarse en el historial o journal aunque no muten la estructura; los rechazos no agregan llamadas exitosas. Restablecer TAD (o Reiniciar, según el módulo) borra la estructura y su historial.

Referencia avanzada por operación

Abre sólo el tema que estás estudiando. Estos detalles distinguen el contrato C, su representación visual y las funciones auxiliares de la aplicación.

Representación y Ver arreglo
  • Min-heap representado en arreglo y visualizado también como árbol casi completo.
  • Ver arreglo copia todos los valores en su orden interno, no en orden total.
  • El main reproducible reserva la copia según la cantidad actual y libera esa memoria; si la reserva falla, termina sin mostrar una copia parcial.
  • Las consultas exitosas repetidas se conservan al recargar; restaurarlas no vuelve a ejecutarlas.
Consultas: Ver raíz y Ver arreglo
  • En Insertar y Extraer raíz identifica los intercambios que restauran la prioridad padre-hijos.
  • Ver raíz y Ver arreglo son consultas: no intercambian ni modifican el arreglo del montículo.
  • En estas dos consultas, el resaltado corresponde a la instrucción ya ejecutada; retroceder cambia la observación, no vuelve a ejecutar una operación.
  • Ver raíz separa el bool de éxito del int de salida, que permanece sin inicializar si falla la guarda.
  • Ver arreglo muestra cada celda del buffer independiente al inicializarse y conserva registros anteriores a free; estos registros no son memoria viva ni punteros utilizables.
  • Los llamadores equivalentes mostrados son auxiliares didácticos, no nuevas funciones del C/H.
Cantidad y capacidad
  • La capacidad es distinta de cantidad: solicitar 16 reserva 20 según el C (base 10 y duplicación); el elemento 21 hace crecer a 40 y extraer elementos no reduce esa capacidad.
  • La vista muestra los valores lógicos hasta cantidad; no afirma que el espacio restante sea cero.
Limpiar y nueva reserva
  • Limpiar destruye el buffer previo aunque cantidad sea cero, asigna datos NULL y luego cantidad y capacidad 0.
  • A continuación inicializa otro buffer: pedir 16 reserva 20 si la reserva tiene éxito; si falla, conserva datos NULL, cantidad 0 y capacidad 0.
  • El retorno void no anuncia el resultado de la reserva.
  • La nueva vida A2 es distinta de A1 aunque el asignador reutilice una dirección.
  • A2 nace sin enteros inicializados; las celdas fuera de cantidad en A1 se muestran como no observadas, no como ceros.
  • Retroceder observa registros anteriores, sin ejecutar free ni reservar otra vez.
Insertar y ascenso
  • En Insertar, datos[cantidad] se escribe antes del ascenso y cantidad++ ocurre después.
  • Los dibujos siguen las celdas escritas, incluyendo esa celda pendiente fuera de cantidad; no confunden cantidad con celdas escritas.
  • Comparar recibe enteros por valor; intercambiar recibe alias int * y ejecuta temp=*a, *a=*b y *b=temp por separado: hay duplicación transitoria.
  • División entera C trunca hacia cero: (-1)/2 da 0.
  • El cortocircuito evita comparar si indice es 0.
Crecer con realloc
  • Si realloc crece, el buffer previo termina su vida; el nuevo conserva el prefijo y se publica antes de asignar capacidad.
  • Entre esas instrucciones datos es indeterminado y los dibujos se suspenden.
  • Si realloc falla, la inserción devuelve false y conserva contenido/cantidad/capacidad.
  • El bool se muestra al retornar; avanzar o retroceder nunca vuelve a insertar.
Extraer raíz y descenso
  • En Extraer raíz, el entero externo se escribe primero; después disminuye cantidad.
  • Si aún hay elementos, la antigua cola se copia en la raíz antes del descenso.
  • Las dos vistas siguen el prefijo lógico; la tarjeta de memoria conserva la cola inicializada fuera de cantidad, sin dibujarla como otro elemento.
  • No se libera A1 ni se reduce capacidad.
  • Cada hijo se compara solo si está dentro de cantidad; el empate no selecciona ese hijo.
  • El descenso muestra tres escrituras por intercambio.
  • Si el heap está vacío, devuelve false sin escribir salida; no hay descenso.
  • El entero de salida es distinto del bool; retroceder no extrae de nuevo.

Código fuente completo del TAD

Descarga el archivo C principal de Montículo binario y su encabezado para estudiarlo o compilarlo en tu equipo.

Referencia: estructura del TAD en C
typedef enum {
    MONTICULO_MIN = 0,  /**< Menor valor tiene prioridad; comparacion estricta. */
    MONTICULO_MAX = 1   /**< Mayor valor tiene prioridad; comparacion estricta. */
} TipoMonticulo;

typedef struct {
    int *datos; /**< Arreglo de datos del montículo. */
    int cantidad; /**< Número de elementos almacenados. */
    int capacidad; /**< Capacidad reservada de la estructura. */
    TipoMonticulo tipo; /**< Tipo de montículo mínimo o máximo. */
} MonticuloBinario;

Código C y llamadores equivalentes

Insertar · código C

Inserta respetando reglas de orden/balance del TAD y actualiza punteros estructurales necesarios.

/**
 * @brief Restaura la propiedad del montículo moviendo un elemento hacia arriba.
 * 
 * @details Compara el elemento en el `indice` dado con su padre. Si tiene mayor
 *          prioridad (según `comparar`), los intercambia y repite el proceso.
 * 
 * @param[in,out] m      Puntero al montículo.
 * @param[in]     indice Índice del elemento que se va a subir.
 * 
 * @note Complejidad temporal: O(log N).
 * @pre m y datos validos; indice >= 0 identifica slot inicializado dentro de capacidad (puede ser cantidad antes de incrementarla). Antecesores satisfacen propiedad salvo ese slot.
 * @note No comprueba NULL/limites ni reserva/libera. Compara estrictamente con padres; empates no se intercambian.
 */
static void heapify_up(MonticuloBinario *m, int indice) {
    int padre = (indice - 1) / 2;
    while (indice > 0 && comparar(m->tipo, m->datos[indice], m->datos[padre])) {
        intercambiar(&m->datos[indice], &m->datos[padre]);
        indice = padre;
        padre = (indice - 1) / 2;
    }
}

/**
 * @brief Inserta un valor en el montículo preservando su propiedad.
 *
 * @details Si se alcanza la capacidad máxima del arreglo interno, este se
 *          redimensiona al doble automáticamente utilizando `realloc`.
 *
 * @param[in,out] m     Puntero al montículo.
 * @param[in]     valor Valor entero a insertar.
 *
 * @return @c true  si la inserción fue exitosa.
 * @return @c false si hubo un error de memoria o `m` es nulo.
 *
 * @details El retorno es estado bool, no el entero insertado. Si m es NULL o
 * falla la reserva, devuelve false sin cambiar datos, cantidad, capacidad ni valores.
 * En exito escribe datos[cantidad], restaura la prioridad con heapify_up y solo
 * entonces incrementa cantidad. La celda escrita aun esta fuera de cantidad durante el ascenso.
 * El intercambio son tres asignaciones, no una mutacion atomica.
 * Un realloc exitoso termina la vida previa aunque reutilice direccion: no evaluar
 * alias anteriores; el temporal se publica en datos antes de asignar capacidad.
 * Si realloc falla, el buffer previo sigue vivo. Las celdas nuevas no se inicializan
 * por realloc; solo la asignacion del valor garantiza esa celda inicializada.
 * @note Complejidad temporal: O(log N) promedio, O(N) si requiere redimensionar.
 */
bool monticulo_insertar(MonticuloBinario *m, int valor) {
    if (m == NULL) return false;

    if (!asegurar_capacidad(m, m->cantidad + 1)) {
        return false;
    }

    m->datos[m->cantidad] = valor;
    heapify_up(m, m->cantidad);
    m->cantidad++;
    return true;
}
Extraer raíz · código C

Copia el mínimo a la salida, reduce la cantidad y repara el orden bajando el nuevo valor de la raíz. Distingue el valor extraído del indicador de éxito.

/**
 * @brief Restaura la propiedad del montículo hundiendo un elemento hacia abajo.
 * 
 * @details Compara el elemento con sus dos hijos. Si alguno de los hijos tiene
 *          mayor prioridad, intercambia el elemento con el hijo más prioritario
 *          y repite el proceso hacia abajo.
 * 
 * @param[in,out] m      Puntero al montículo.
 * @param[in]     indice Índice del elemento que se va a hundir.
 * 
 * @note Complejidad temporal: O(log N).
 * @pre m y datos validos; 0 <= indice < cantidad <= capacidad; subarboles de hijos ya son heaps. Cada 2*indice+2 visitado debe ser representable en int.
 * @note No comprueba NULL ni corrige metadata. Compara hijo izquierdo antes de derecho y usa mejora estricta; no reserva/libera. No certifica indices enormes.
 */
static void heapify_down(MonticuloBinario *m, int indice) {
    int hijo_izq, hijo_der, seleccionado;

    while (1) {
        hijo_izq = 2 * indice + 1;
        hijo_der = 2 * indice + 2;
        seleccionado = indice;

        if (hijo_izq < m->cantidad && comparar(m->tipo, m->datos[hijo_izq], m->datos[seleccionado])) {
            seleccionado = hijo_izq;
        }

        if (hijo_der < m->cantidad && comparar(m->tipo, m->datos[hijo_der], m->datos[seleccionado])) {
            seleccionado = hijo_der;
        }

        if (seleccionado != indice) {
            intercambiar(&m->datos[indice], &m->datos[seleccionado]);
            indice = seleccionado;
        } else {
            break;
        }
    }
}

/**
 * @brief Extrae la raíz del montículo (el menor o mayor elemento según el tipo).
 *
 * @details Retorna la raíz, la reemplaza por el último elemento del árbol
 *          y restablece la propiedad hundiendo este nuevo elemento.
 *
 * @param[in,out] m         Puntero al montículo.
 * @param[out]    resultado Puntero donde se escribirá el valor extraído.
 *
 * @return @c true si se extrajo correctamente.
 *         @c false si el montículo está vacío o nulo.
 *
 * @note Complejidad temporal: O(log N).
 * @details El retorno bool indica estado, no el entero extraido. Si m es NULL,
 * cantidad es cero o resultado es NULL, devuelve false sin escribir la salida
 * ni modificar datos, cantidad o capacidad; la condicion usa cortocircuito.
 * En exito escribe *resultado antes de reducir cantidad. Si queda algun elemento,
 * copia la antigua cola en la raiz y ejecuta heapify_down. Cada intercambio son
 * tres asignaciones independientes: pueden existir valores duplicados transitorios.
 * Extraer no libera ni reduce capacidad; la celda de la antigua cola sigue viva
 * e inicializada fuera del prefijo logico. No se debe mostrar como otro elemento.
 * Las comparaciones reciben enteros por valor; el intercambio usa alias int *.
 */
bool monticulo_extraer_raiz(MonticuloBinario *m, int *resultado) {
    if (m == NULL || m->cantidad == 0 || resultado == NULL) return false;
    
    *resultado = m->datos[0];
    m->cantidad--;
    
    if (m->cantidad > 0) {
        m->datos[0] = m->datos[m->cantidad];
        heapify_down(m, 0);
    }
    
    return true;
}
Ver raíz · código C

Retorna el valor de la raíz o tope lógico de la estructura sin mutarla.

/**
 * @brief Consulta la raíz del montículo sin modificar la estructura.
 *
 * @param[in]  m         Puntero constante al montículo.
 * @param[out] resultado Puntero donde se almacenará el valor de la raíz.
 *
 * @return @c true si el montículo no está vacío y se obtuvo el resultado.
 *         @c false si está vacío o los punteros son nulos.
 *
 * @note Complejidad temporal: O(1).
 * @details Si devuelve false, no escribe resultado. Si devuelve true,
 * escribe el entero de datos[0], distinto del estado booleano retornado.
 * No reserva memoria ni cambia los datos, cantidad o capacidad del monticulo.
 */
bool monticulo_raiz(const MonticuloBinario *m, int *resultado) {
    if (m == NULL || m->cantidad == 0 || resultado == NULL) return false;
    *resultado = m->datos[0];
    return true;
}
Ver arreglo · código C

Exporta el contenido interno a una representación lineal para inspección externa.

/* Copia didactica del arreglo interno del monticulo. */
/* Requiere <stdlib.h>; reserva solo si hay elementos. */
int cantidad = monticulo_cantidad(&monticulo);
int *buffer = cantidad > 0 ? malloc((size_t)cantidad * sizeof *buffer) : NULL;
if (cantidad > 0 && buffer == NULL) {
    /* Sin memoria: no copiar ni presentar un resultado incompleto. */
    return EXIT_FAILURE;
}
int usados = monticulo_copiar_valores(&monticulo, buffer, cantidad);
for (int i = 0; i < usados; i++) {
    /* buffer[i] contiene el valor en el indice i, no orden total. */
}
free(buffer);
Limpiar · código C

Libera el arreglo interno del montículo y lo reinicializa para volver a operar; este TAD no utiliza nodos enlazados.

/**
 * @brief Libera completamente la memoria reservada por el montículo.
 *
 * @param[in,out] m Puntero al montículo a destruir.
 *
 * @details Si m es NULL no hace nada. Si datos no es NULL libera ese buffer,
 * incluso con cantidad 0, y asigna datos NULL antes de reiniciar los contadores.
 * No modifica tipo. Los alias del buffer liberado no se deben evaluar ni usar.
 * Esta funcion no reinicializa ni reserva memoria: el llamador visible Limpiar
 * invoca despues monticulo_inicializar con capacidad solicitada 16 (reserva 20
 * si tiene exito; si falla, datos NULL y capacidad 0). Es otra vida de memoria.
 * @note Tras ejecutarse, la cantidad y la capacidad se reinician a 0.
 * @note Complejidad temporal: O(1).
 */
void monticulo_destruir(MonticuloBinario *m) {
    if (m != NULL) {
        if (m->datos) {
            free(m->datos);
            m->datos = NULL;
        }
        m->cantidad = 0;
        m->capacidad = 0;
    }
}

/* Reinicio recomendado del TAD tras liberar memoria interna */
/**
 * @brief Inicializa un montículo vacío con una capacidad inicial y un tipo.
 * 
 * @param[out] m                 Puntero al montículo a inicializar.
 * @param[in]  tipo              Tipo de montículo (`MONTICULO_MIN` o `MONTICULO_MAX`).
 * @param[in]  capacidad_inicial Capacidad base a reservar en el arreglo. Si es <= 0, se usa 10 por defecto.
 * 
 * @pre Estructura escribible sin reserva propietaria pendiente; tamanos de reserva representables en size_t.
 * @note NULL m se ignora. Reinicializar no libera datos anteriores: destruir antes. Fija tipo, cantidad=0 y datos=NULL; capacidad inicial <=0 usa 10.
 * @note Fallo reserva deja cantidad=capacidad=0, datos=NULL, tipo conservado; retorno void no comunica exito. No valida enum.
 * @note La seleccion de capacidad por duplicacion realiza O(log capacidad_objetivo) iteraciones en el peor caso; el coste del allocator no se certifica.
 */
void monticulo_inicializar(MonticuloBinario *m, TipoMonticulo tipo, int capacidad_inicial) {
    int capacidad_objetivo;

    if (m == NULL) return;
    m->tipo = tipo;
    m->cantidad = 0;
    m->capacidad = 0;
    m->datos = NULL;

    capacidad_objetivo = (capacidad_inicial > 0) ? capacidad_inicial : MONTICULO_CAPACIDAD_POR_DEFECTO;
    if (!asegurar_capacidad(m, capacidad_objetivo)) {
        m->capacidad = 0;
    }
}

Ir a la visualización