11 - Ejemplo práctico: árbol de decisiones para IA

Los árboles de decisiones son una de las primeras herramientas de Machine Learning. En esta versión hard-coded creamos un clasificador de frutas que decide según preguntas simples. El árbol tiene varias capas para que el recorrido sea interesante: la raíz pregunta “¿Color?”, luego cada rama examina tamaño, textura, olor o sabor hasta llegar a una hoja con la clasificación final.

11.1 Concepto general

Necesitamos un sistema que responda preguntas a partir de un objeto observado (color, tamaño, textura, olor, sabor). Cada nodo del árbol representa una regla. Las hojas contienen el nombre de la fruta. No usamos entrenamiento: las reglas están programadas a mano para ilustrar cómo funcionan los recorridos.

11.2 Modelo de nodo

El árbol se modela con una estructura que almacena el tipo de pregunta, el texto descriptivo y la lista de opciones que apuntan a otros nodos. Las hojas usan el mismo struct pero marcan NODO_CLASIFICACION.

typedef enum {
  NODO_COLOR,
  NODO_TAMANO,
  NODO_TEXTURA,
  NODO_OLOR,
  NODO_SABOR,
  NODO_CLASIFICACION
} TipoDecision;

typedef struct Opcion {
  int valor;
  char etiqueta[24];
  struct DecisionNode *destino;
  struct Opcion *siguiente;
} Opcion;

typedef struct DecisionNode {
  TipoDecision tipo;
  char pregunta[64];
  char clasificacion[40];
  struct Opcion *primerHijo;
} DecisionNode;

DecisionNode *crearNodoPregunta(TipoDecision tipo, const char *pregunta) {
  DecisionNode *n = calloc(1, sizeof(DecisionNode));
  if (!n) return NULL;
  n->tipo = tipo;
  if (pregunta) strncpy(n->pregunta, pregunta, sizeof(n->pregunta) - 1);
  return n;
}

DecisionNode *crearNodoClasificacion(const char *nombre) {
  DecisionNode *n = calloc(1, sizeof(DecisionNode));
  if (!n) return NULL;
  n->tipo = NODO_CLASIFICACION;
  strncpy(n->clasificacion, nombre, sizeof(n->clasificacion) - 1);
  return n;
}

void agregarOpcion(DecisionNode *padre, int valor, const char *etiqueta, DecisionNode *destino) {
  if (!padre || !destino) return;
  Opcion *op = calloc(1, sizeof(Opcion));
  if (!op) return;
  op->valor = valor;
  strncpy(op->etiqueta, etiqueta, sizeof(op->etiqueta) - 1);
  op->destino = destino;
  if (!padre->primerHijo) {
    padre->primerHijo = op;
  } else {
    Opcion *actual = padre->primerHijo;
    while (actual->siguiente) actual = actual->siguiente;
    actual->siguiente = op;
  }
}

11.3 Atributos observables

El objeto a clasificar se almacena en un struct Objeto. Cada atributo se expresa como un enumerado para evitar comparaciones de cadenas.

typedef enum { COLOR_ROJO, COLOR_AMARILLO, COLOR_VERDE } Color;
typedef enum { TAMANO_PEQUENO, TAMANO_MEDIANO, TAMANO_GRANDE } Tamano;
typedef enum { TEXTURA_LISA, TEXTURA_RUGOSA } Textura;
typedef enum { OLOR_NEUTRO, OLOR_FRAGANTE } Olor;
typedef enum { SABOR_DULCE, SABOR_ACIDO } Sabor;

typedef struct {
  Color color;
  Tamano tamano;
  Textura textura;
  Olor olor;
  Sabor sabor;
} Objeto;

11.4 Construcción del árbol

Partimos de la pregunta Color? y agregamos ramas lo suficientemente profundas para que el árbol cubra varias frutas.

DecisionNode *crearArbolFrutas(void) {
  DecisionNode *raiz = crearNodoPregunta(NODO_COLOR, "Color?");

  DecisionNode *nRojo = crearNodoPregunta(NODO_TAMANO, "Tamanio?");
  DecisionNode *nAmarillo = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  DecisionNode *nVerde = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(raiz, COLOR_ROJO, "Rojo", nRojo);
  agregarOpcion(raiz, COLOR_AMARILLO, "Amarillo", nAmarillo);
  agregarOpcion(raiz, COLOR_VERDE, "Verde", nVerde);

  DecisionNode *rojoGrande = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  DecisionNode *rojoMediano = crearNodoPregunta(NODO_OLOR, "Olor?");
  DecisionNode *rojoPequeno = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(nRojo, TAMANO_GRANDE, "Grande", rojoGrande);
  agregarOpcion(nRojo, TAMANO_MEDIANO, "Mediano", rojoMediano);
  agregarOpcion(nRojo, TAMANO_PEQUENO, "Pequeno", rojoPequeno);

  agregarOpcion(rojoGrande, TEXTURA_LISA, "Lisa", crearNodoClasificacion("Manzana Crimson"));
  DecisionNode *rojoGrandeRugosa = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(rojoGrande, TEXTURA_RUGOSA, "Rugosa", rojoGrandeRugosa);
  agregarOpcion(rojoGrandeRugosa, SABOR_DULCE, "Dulce", crearNodoClasificacion("Granada dulce"));
  agregarOpcion(rojoGrandeRugosa, SABOR_ACIDO, "Acido", crearNodoClasificacion("Pomelo rosado"));

  agregarOpcion(rojoMediano, OLOR_FRAGANTE, "Fragante", crearNodoClasificacion("Frutilla fragante"));
  DecisionNode *rojoMedianoNeutro = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(rojoMediano, OLOR_NEUTRO, "Neutro", rojoMedianoNeutro);
  agregarOpcion(rojoMedianoNeutro, SABOR_DULCE, "Dulce", crearNodoClasificacion("Ciruela roja"));
  agregarOpcion(rojoMedianoNeutro, SABOR_ACIDO, "Acido", crearNodoClasificacion("Arandano rojo"));

  DecisionNode *rojoPequenoTextura = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  agregarOpcion(rojoPequeno, SABOR_DULCE, "Dulce", rojoPequenoTextura);
  agregarOpcion(rojoPequeno, SABOR_ACIDO, "Acido", crearNodoClasificacion("Grosella roja"));
  agregarOpcion(rojoPequenoTextura, TEXTURA_LISA, "Lisa", crearNodoClasificacion("Cereza"));
  agregarOpcion(rojoPequenoTextura, TEXTURA_RUGOSA, "Rugosa", crearNodoClasificacion("Frambuesa"));

  DecisionNode *amarilloLisa = crearNodoPregunta(NODO_TAMANO, "Tamanio?");
  DecisionNode *amarilloRugosa = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(nAmarillo, TEXTURA_LISA, "Lisa", amarilloLisa);
  agregarOpcion(nAmarillo, TEXTURA_RUGOSA, "Rugosa", amarilloRugosa);
  agregarOpcion(amarilloLisa, TAMANO_GRANDE, "Grande", crearNodoClasificacion("Papaya dorada"));
  agregarOpcion(amarilloLisa, TAMANO_MEDIANO, "Mediano", crearNodoClasificacion("Mango ataulfo"));
  agregarOpcion(amarilloLisa, TAMANO_PEQUENO, "Pequeno", crearNodoClasificacion("Banana baby"));
  agregarOpcion(amarilloRugosa, SABOR_DULCE, "Dulce", crearNodoClasificacion("Maracuya amarilla"));
  agregarOpcion(amarilloRugosa, SABOR_ACIDO, "Acido", crearNodoClasificacion("Limon rugoso"));

  DecisionNode *verdeAcido = crearNodoPregunta(NODO_TAMANO, "Tamanio?");
  DecisionNode *verdeDulce = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  agregarOpcion(nVerde, SABOR_ACIDO, "Acido", verdeAcido);
  agregarOpcion(nVerde, SABOR_DULCE, "Dulce", verdeDulce);
  agregarOpcion(verdeAcido, TAMANO_PEQUENO, "Pequeno", crearNodoClasificacion("Lima"));
  DecisionNode *verdeAcidoGrande = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  agregarOpcion(verdeAcido, TAMANO_GRANDE, "Grande", verdeAcidoGrande);
  agregarOpcion(verdeAcidoGrande, TEXTURA_LISA, "Lisa", crearNodoClasificacion("Manzana verde"));
  agregarOpcion(verdeAcidoGrande, TEXTURA_RUGOSA, "Rugosa", crearNodoClasificacion("Pomelo verde"));
  agregarOpcion(verdeDulce, TEXTURA_LISA, "Lisa", crearNodoClasificacion("Uva verde"));
  DecisionNode *verdeDulceRugosa = crearNodoPregunta(NODO_TAMANO, "Tamanio?");
  agregarOpcion(verdeDulce, TEXTURA_RUGOSA, "Rugosa", verdeDulceRugosa);
  agregarOpcion(verdeDulceRugosa, TAMANO_PEQUENO, "Pequeno", crearNodoClasificacion("Kiwi"));
  agregarOpcion(verdeDulceRugosa, TAMANO_MEDIANO, "Mediano", crearNodoClasificacion("Melon verde"));

  return raiz;
}

11.5 Funciones de clasificación

Mantenemos la función clasificar para pruebas automáticas con structs y agregamos clasificarInteractivo para que la aplicación pregunte al usuario y permita elegir la opción correspondiente en cada nodo.

int valorAtributo(const Objeto *obj, TipoDecision tipo) {
  switch (tipo) {
    case NODO_COLOR: return obj->color;
    case NODO_TAMANO: return obj->tamano;
    case NODO_TEXTURA: return obj->textura;
    case NODO_OLOR: return obj->olor;
    case NODO_SABOR: return obj->sabor;
    default: return -1;
  }
}

const char *clasificar(const DecisionNode *nodo, const Objeto *obj) {
  if (!nodo || !obj) return "Desconocido";
  if (nodo->tipo == NODO_CLASIFICACION) {
    return nodo->clasificacion;
  }
  int valor = valorAtributo(obj, nodo->tipo);
  for (Opcion *op = nodo->primerHijo; op; op = op->siguiente) {
    if (op->valor == valor) {
      return clasificar(op->destino, obj);
    }
  }
  return "Desconocido";
}

const char *clasificarInteractivo(DecisionNode *nodo) {
  if (!nodo) return "Desconocido";
  DecisionNode *actual = nodo;
  char entrada[32];
  while (actual && actual->tipo != NODO_CLASIFICACION) {
    printf("\n%s\n", actual->pregunta);
    int indice = 1;
    for (Opcion *op = actual->primerHijo; op; op = op->siguiente, ++indice) {
      printf("  %d) %s\n", indice, op->etiqueta);
    }
    printf("Selecciona una opcion: ");
    if (!fgets(entrada, sizeof(entrada), stdin)) return "Entrada invalida";
    int seleccion = (int)strtol(entrada, NULL, 10);
    if (seleccion <= 0) {
      puts("Opcion invalida, intenta de nuevo.");
      continue;
    }
    Opcion *elegida = actual->primerHijo;
    for (int i = 1; elegida && i < seleccion; ++i) {
      elegida = elegida->siguiente;
    }
    if (!elegida) {
      puts("Seleccion fuera de rango.");
      continue;
    }
    actual = elegida->destino;
  }
  return actual ? actual->clasificacion : "Desconocido";
}

11.6 Pruebas sugeridas

  • Crear objetos con cada combinación de color principal y confirmar que las hojas retornadas tengan sentido.
  • Agregar una rama nueva para color naranja y verificar que las funciones no requieren cambios extra.
  • Imprimir las rutas tomadas (opciones seleccionadas) para construir una explicación del modelo.

11.7 Código completo para CLion

El siguiente programa arma el árbol, clasifica varios objetos, ofrece una sesión interactiva de preguntas y limpia la memoria al final.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#endif

typedef enum { COLOR_ROJO, COLOR_AMARILLO, COLOR_VERDE } Color;
typedef enum { TAMANO_PEQUENO, TAMANO_MEDIANO, TAMANO_GRANDE } Tamano;
typedef enum { TEXTURA_LISA, TEXTURA_RUGOSA } Textura;
typedef enum { OLOR_NEUTRO, OLOR_FRAGANTE } Olor;
typedef enum { SABOR_DULCE, SABOR_ACIDO } Sabor;

typedef struct {
  Color color;
  Tamano tamano;
  Textura textura;
  Olor olor;
  Sabor sabor;
} Objeto;

typedef enum {
  NODO_COLOR,
  NODO_TAMANO,
  NODO_TEXTURA,
  NODO_OLOR,
  NODO_SABOR,
  NODO_CLASIFICACION
} TipoDecision;

typedef struct DecisionNode DecisionNode;

typedef struct Opcion {
  int valor;
  char etiqueta[24];
  DecisionNode *destino;
  struct Opcion *siguiente;
} Opcion;

struct DecisionNode {
  TipoDecision tipo;
  char pregunta[64];
  char clasificacion[40];
  Opcion *primerHijo;
};

DecisionNode *crearNodoPregunta(TipoDecision tipo, const char *pregunta) {
  DecisionNode *n = calloc(1, sizeof(DecisionNode));
  if (!n) return NULL;
  n->tipo = tipo;
  if (pregunta) strncpy(n->pregunta, pregunta, sizeof(n->pregunta) - 1);
  return n;
}

DecisionNode *crearNodoClasificacion(const char *nombre) {
  DecisionNode *n = calloc(1, sizeof(DecisionNode));
  if (!n) return NULL;
  n->tipo = NODO_CLASIFICACION;
  strncpy(n->clasificacion, nombre, sizeof(n->clasificacion) - 1);
  return n;
}

void agregarOpcion(DecisionNode *padre, int valor, const char *etiqueta, DecisionNode *destino) {
  if (!padre || !destino) return;
  Opcion *op = calloc(1, sizeof(Opcion));
  if (!op) return;
  op->valor = valor;
  strncpy(op->etiqueta, etiqueta, sizeof(op->etiqueta) - 1);
  op->destino = destino;
  if (!padre->primerHijo) {
    padre->primerHijo = op;
  } else {
    Opcion *actual = padre->primerHijo;
    while (actual->siguiente) actual = actual->siguiente;
    actual->siguiente = op;
  }
}

int valorAtributo(const Objeto *obj, TipoDecision tipo) {
  switch (tipo) {
    case NODO_COLOR: return obj->color;
    case NODO_TAMANO: return obj->tamano;
    case NODO_TEXTURA: return obj->textura;
    case NODO_OLOR: return obj->olor;
    case NODO_SABOR: return obj->sabor;
    default: return -1;
  }
}

const char *clasificar(const DecisionNode *nodo, const Objeto *obj) {
  if (!nodo || !obj) return "Desconocido";
  if (nodo->tipo == NODO_CLASIFICACION) return nodo->clasificacion;
  int valor = valorAtributo(obj, nodo->tipo);
  for (Opcion *op = nodo->primerHijo; op; op = op->siguiente) {
    if (op->valor == valor) return clasificar(op->destino, obj);
  }
  return "Desconocido";
}

const char *clasificarInteractivo(DecisionNode *nodo) {
  if (!nodo) return "Desconocido";
  DecisionNode *actual = nodo;
  char entrada[32];
  while (actual && actual->tipo != NODO_CLASIFICACION) {
    printf("\n%s\n", actual->pregunta);
    int indice = 1;
    for (Opcion *op = actual->primerHijo; op; op = op->siguiente, ++indice) {
      printf("  %d) %s\n", indice, op->etiqueta);
    }
    printf("Selecciona una opcion: ");
    if (!fgets(entrada, sizeof(entrada), stdin)) return "Entrada invalida";
    long seleccion = strtol(entrada, NULL, 10);
    if (seleccion <= 0) {
      puts("Opcion invalida, intenta nuevamente.");
      continue;
    }
    Opcion *opcionElegida = actual->primerHijo;
    for (int i = 1; opcionElegida && i < seleccion; ++i) {
      opcionElegida = opcionElegida->siguiente;
    }
    if (!opcionElegida) {
      puts("Seleccion fuera de rango.");
      continue;
    }
    actual = opcionElegida->destino;
  }
  return actual ? actual->clasificacion : "Desconocido";
}

DecisionNode *crearArbolFrutas(void) {
  DecisionNode *raiz = crearNodoPregunta(NODO_COLOR, "Color?");

  DecisionNode *nRojo = crearNodoPregunta(NODO_TAMANO, "Tamanio?");
  DecisionNode *nAmarillo = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  DecisionNode *nVerde = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(raiz, COLOR_ROJO, "Rojo", nRojo);
  agregarOpcion(raiz, COLOR_AMARILLO, "Amarillo", nAmarillo);
  agregarOpcion(raiz, COLOR_VERDE, "Verde", nVerde);

  DecisionNode *rojoGrande = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  DecisionNode *rojoMediano = crearNodoPregunta(NODO_OLOR, "Olor?");
  DecisionNode *rojoPequeno = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(nRojo, TAMANO_GRANDE, "Grande", rojoGrande);
  agregarOpcion(nRojo, TAMANO_MEDIANO, "Mediano", rojoMediano);
  agregarOpcion(nRojo, TAMANO_PEQUENO, "Pequeno", rojoPequeno);

  agregarOpcion(rojoGrande, TEXTURA_LISA, "Lisa", crearNodoClasificacion("Manzana Crimson"));
  DecisionNode *rojoGrandeRugosa = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(rojoGrande, TEXTURA_RUGOSA, "Rugosa", rojoGrandeRugosa);
  agregarOpcion(rojoGrandeRugosa, SABOR_DULCE, "Dulce", crearNodoClasificacion("Granada dulce"));
  agregarOpcion(rojoGrandeRugosa, SABOR_ACIDO, "Acido", crearNodoClasificacion("Pomelo rosado"));

  agregarOpcion(rojoMediano, OLOR_FRAGANTE, "Fragante", crearNodoClasificacion("Frutilla fragante"));
  DecisionNode *rojoMedianoNeutro = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(rojoMediano, OLOR_NEUTRO, "Neutro", rojoMedianoNeutro);
  agregarOpcion(rojoMedianoNeutro, SABOR_DULCE, "Dulce", crearNodoClasificacion("Ciruela roja"));
  agregarOpcion(rojoMedianoNeutro, SABOR_ACIDO, "Acido", crearNodoClasificacion("Arandano rojo"));

  DecisionNode *rojoPequenoTextura = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  agregarOpcion(rojoPequeno, SABOR_DULCE, "Dulce", rojoPequenoTextura);
  agregarOpcion(rojoPequeno, SABOR_ACIDO, "Acido", crearNodoClasificacion("Grosella roja"));
  agregarOpcion(rojoPequenoTextura, TEXTURA_LISA, "Lisa", crearNodoClasificacion("Cereza"));
  agregarOpcion(rojoPequenoTextura, TEXTURA_RUGOSA, "Rugosa", crearNodoClasificacion("Frambuesa"));

  DecisionNode *amarilloLisa = crearNodoPregunta(NODO_TAMANO, "Tamanio?");
  DecisionNode *amarilloRugosa = crearNodoPregunta(NODO_SABOR, "Sabor?");
  agregarOpcion(nAmarillo, TEXTURA_LISA, "Lisa", amarilloLisa);
  agregarOpcion(nAmarillo, TEXTURA_RUGOSA, "Rugosa", amarilloRugosa);
  agregarOpcion(amarilloLisa, TAMANO_GRANDE, "Grande", crearNodoClasificacion("Papaya dorada"));
  agregarOpcion(amarilloLisa, TAMANO_MEDIANO, "Mediano", crearNodoClasificacion("Mango ataulfo"));
  agregarOpcion(amarilloLisa, TAMANO_PEQUENO, "Pequeno", crearNodoClasificacion("Banana baby"));
  agregarOpcion(amarilloRugosa, SABOR_DULCE, "Dulce", crearNodoClasificacion("Maracuya amarilla"));
  agregarOpcion(amarilloRugosa, SABOR_ACIDO, "Acido", crearNodoClasificacion("Limon rugoso"));

  DecisionNode *verdeAcido = crearNodoPregunta(NODO_TAMANO, "Tamanio?");
  DecisionNode *verdeDulce = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  agregarOpcion(nVerde, SABOR_ACIDO, "Acido", verdeAcido);
  agregarOpcion(nVerde, SABOR_DULCE, "Dulce", verdeDulce);
  agregarOpcion(verdeAcido, TAMANO_PEQUENO, "Pequeno", crearNodoClasificacion("Lima"));
  DecisionNode *verdeAcidoGrande = crearNodoPregunta(NODO_TEXTURA, "Textura?");
  agregarOpcion(verdeAcido, TAMANO_GRANDE, "Grande", verdeAcidoGrande);
  agregarOpcion(verdeAcidoGrande, TEXTURA_LISA, "Lisa", crearNodoClasificacion("Manzana verde"));
  agregarOpcion(verdeAcidoGrande, TEXTURA_RUGOSA, "Rugosa", crearNodoClasificacion("Pomelo verde"));
  agregarOpcion(verdeDulce, TEXTURA_LISA, "Lisa", crearNodoClasificacion("Uva verde"));
  DecisionNode *verdeDulceRugosa = crearNodoPregunta(NODO_TAMANO, "Tamanio?");
  agregarOpcion(verdeDulce, TEXTURA_RUGOSA, "Rugosa", verdeDulceRugosa);
  agregarOpcion(verdeDulceRugosa, TAMANO_PEQUENO, "Pequeno", crearNodoClasificacion("Kiwi"));
  agregarOpcion(verdeDulceRugosa, TAMANO_MEDIANO, "Mediano", crearNodoClasificacion("Melon verde"));

  return raiz;
}

void liberarArbol(DecisionNode *nodo) {
  if (!nodo) return;
  for (Opcion *op = nodo->primerHijo; op; op = op->siguiente) {
    liberarArbol(op->destino);
  }
  Opcion *actual = nodo->primerHijo;
  while (actual) {
    Opcion *sig = actual->siguiente;
    free(actual);
    actual = sig;
  }
  free(nodo);
}

int main(void) {
#ifdef _WIN32
  SetConsoleOutputCP(CP_UTF8);
  SetConsoleCP(CP_UTF8);
#endif
  srand((unsigned)time(NULL));
  DecisionNode *raiz = crearArbolFrutas();

  Objeto muestras[] = {
    {COLOR_ROJO, TAMANO_GRANDE, TEXTURA_LISA, OLOR_NEUTRO, SABOR_DULCE},
    {COLOR_ROJO, TAMANO_PEQUENO, TEXTURA_RUGOSA, OLOR_NEUTRO, SABOR_DULCE},
    {COLOR_AMARILLO, TAMANO_MEDIANO, TEXTURA_LISA, OLOR_NEUTRO, SABOR_DULCE},
    {COLOR_AMARILLO, TAMANO_PEQUENO, TEXTURA_RUGOSA, OLOR_NEUTRO, SABOR_ACIDO},
    {COLOR_VERDE, TAMANO_PEQUENO, TEXTURA_RUGOSA, OLOR_NEUTRO, SABOR_DULCE},
    {COLOR_VERDE, TAMANO_GRANDE, TEXTURA_LISA, OLOR_NEUTRO, SABOR_ACIDO}
  };

  const char *nombres[] = {"Caso A", "Caso B", "Caso C", "Caso D", "Caso E", "Caso F"};
  puts("=== Clasificacion automatica ===");
  for (size_t i = 0; i < sizeof(muestras)/sizeof(muestras[0]); ++i) {
    printf("%s => %s\n", nombres[i], clasificar(raiz, &muestras[i]));
  }

  puts("\n=== Sesion interactiva ===");
  const char *resultado = clasificarInteractivo(raiz);
  printf("Resultado: %s\n", resultado);

  liberarArbol(raiz);
  return 0;
}
Flujo del árbol de decisiones de frutas
Mapa del árbol de decisiones con sus preguntas y clasificaciones finales.