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
- Relaciona cada posición del arreglo con su nodo en el árbol.
- Al insertar o extraer, sigue los intercambios entre padres e hijos.
- 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
- Prepara una operación y predice qué cambiará.
- Activa Paso a paso antes de ejecutar si quieres ver la traza; sin ese modo obtienes el resultado final.
- Relaciona el dibujo con la línea C resaltada: acaba de ejecutarse. Una condición puede no cambiar ningún nodo.
- 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.
- 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. - 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.
- Elige Cargar valores para añadirlos en orden con resultado final, o Agregar paso a paso para estudiar cada inserción.
- 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
- No existe en el TAD una operación pública para cambiar a max-heap en caliente.
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;
}
}