Sesión 10

Problemas iterativos y estructuras de datos

Unidad 2 · De ciclos aislados a soluciones completas con arreglos

Navegación: ← → · Home / End · F pantalla completa · Táctil: desliza

Propósito de la sesión

De ciclos sueltos a sistemas que procesan colecciones

  • Conectar los ciclos básicos (for, while, do-while) con problemas que procesan conjuntos de datos, no solo valores aislados.
  • Distinguir con rigor cuándo conviene trabajar sin estructura de almacenamiento y cuándo es preferible usar arreglos o listas.
  • Diseñar soluciones iterativas completas que combinan lectura de datos, validación, almacenamiento y cálculo de métricas (conteos, sumas, máximos y mínimos).
  • Construir la base algorítmica de la tarea “Resolviendo un problema con ciclos mediante una solución iterativa en C#” de la Unidad 2. [file:54]
Recordatorio fino: for, while, do-while

Cuándo usar cada ciclo

  • for: número de iteraciones conocido o fácilmente acotable desde el principio (ej. N calificaciones, 12 meses, 7 días).
  • while: la condición depende de un evento externo o de una lectura (ej. seguir leyendo mientras el usuario no ingrese 0).
  • do-while: siempre debe ejecutarse al menos una vez (ej. mostrar un menú interactivo, solicitar datos con validación repetida).

Patrones internos

  • Contadores: variables que registran cuántas veces ocurre una condición relevante.
  • Acumuladores: variables que agregan valores sucesivos para luego calcular métricas como suma o promedio.
  • Banderas: variables booleanas que representan estados (encontrado / no encontrado; continuar / terminar).
Problema central de la sesión

Enunciado: notas, estadísticas y decisiones

Problema: El usuario ingresa N calificaciones de estudiantes (entre 0.0 y 5.0). Se requiere:

  • Calcular el promedio general del grupo.
  • Determinar la nota más alta y la más baja.
  • Contar cuántas notas corresponden a estudiantes aprobados (≥ 3.0).

Versión extendida: se desea, además, conocer cuántos estudiantes están en una franja específica (ej. entre 4.0 y 5.0).

Este problema está alineado con la tarea “Resolviendo un problema con ciclos mediante una solución iterativa en C#” de la Unidad 2. [file:54]

Versión 1: solución iterativa sin estructuras de datos

Idea algorítmica

  • El usuario decide N al inicio (ej. “¿Cuántas notas va a ingresar?”).
  • Se utiliza un for controlado por un contador que va de 1 a N.
  • Dentro del ciclo se lee una nota, se valida el rango y se actualizan contadores y acumuladores.
  • No se guarda la lista completa de notas: solo se conservan las métricas que interesan.

Variables necesarias

  • int cantidadNotas; (N total).
  • double nota; (lectura actual).
  • double sumaNotas; (acumulador para el promedio).
  • double maxNota, minNota; (extremos).
  • int aprobados; (contador).
Construcción paso a paso sin estructuras de almacenamiento

Borrador de implementación en C#

Console.Write("¿Cuántas notas va a ingresar? "); int cantidadNotas = int.Parse(Console.ReadLine()); double sumaNotas = 0.0; int aprobados = 0; double maxNota = double.MinValue; double minNota = double.MaxValue; for (int i = 1; i <= cantidadNotas; i++) { Console.Write($"Nota #{i}: "); double nota = double.Parse(Console.ReadLine()); // Validación de rango (0.0 a 5.0) podría extraerse a otra función más adelante if (nota < 0.0 || nota > 5.0) { Console.WriteLine("Valor inválido, la nota debe estar entre 0.0 y 5.0."); i--; // Repetir esta iteración continue; } sumaNotas += nota; if (nota >= 3.0) { aprobados++; } if (nota > maxNota) maxNota = nota; if (nota < minNota) minNota = nota; } double promedio = sumaNotas / cantidadNotas; Console.WriteLine($"\nPromedio: {promedio:F2}"); Console.WriteLine($"Nota más alta: {maxNota:F2}"); Console.WriteLine($"Nota más baja: {minNota:F2}"); Console.WriteLine($"Aprobados: {aprobados}");

Observa cómo la combinación de contador, acumulador y comparaciones permite obtener todas las métricas sin guardar cada nota individualmente.

Limitaciones de la solución sin estructuras

¿Qué no podemos hacer fácilmente?

  • Volver a recorrer las notas para aplicar otra lógica de negocio distinta.
  • Ordenar las calificaciones para calcular mediana o percentiles.
  • Revisar el historial individual de cada estudiante (ej. imprimir todas las notas).
  • Aplicar filtros complejos (ej. cuántos están en [2.5, 3.0) y luego revisar solo esos casos).

Decisión de diseño

Mientras las preguntas de negocio se limiten a métricas agregadas simples, esta estrategia es suficiente.

Cuando necesitamos reusar los datos en varias operaciones, se vuelve imprescindible almacenarlos en estructuras como arreglos o listas.

¿Qué es un arreglo? Concepto y analogía

La analogía de las casillas numeradas

Imagina una fila de casillas de correo en un edificio. Cada casilla:

  • Guarda exactamente un valor del mismo tipo que todas las demás.
  • Tiene un número de casilla fijo grabado en la puerta — eso es el índice.
  • Se puede abrir directamente si sabes el número — acceso en tiempo constante O(1).

En C#, un arreglo ocupa un bloque contiguo de memoria RAM, lo que hace el acceso por índice extremadamente rápido.

Propiedades fundamentales

  • Tipo homogéneo: todos los elementos son del mismo tipo (double, int, string…).
  • Índice base 0: el primer elemento está en la posición 0, no en la 1.
  • Tamaño fijo: se define al crear el arreglo con new y no puede cambiar después.
  • Longitud accesible: la propiedad .Length siempre indica cuántos elementos tiene el arreglo.

Visualización en memoria: arreglo de 5 notas

Índice (i) 01234
notas[i] 4.53.21.85.02.7
Estudiante 1 2 3 4 5

⚠ El índice va de 0 a Length − 1. Para 5 elementos: índices 0, 1, 2, 3, 4. El índice 5 provoca IndexOutOfRangeException.

Sintaxis en C#: declarar, crear e inicializar arreglos

Las tres formas de crear un arreglo

// Forma 1: declarar y luego asignar con new double[] notas; notas = new double[5]; // 5 posiciones, todas en 0.0 // Forma 2: declarar y crear en una línea double[] notas = new double[5]; // Forma 3: declarar con valores iniciales (literal) double[] notas = { 4.5, 3.2, 1.8, 5.0, 2.7 }; // Forma 3 alternativa (equivalente) double[] notas = new double[] { 4.5, 3.2, 1.8, 5.0, 2.7 };

Leer y escribir elementos

double[] notas = new double[5]; // Escribir en una posición notas[0] = 4.5; // primer elemento notas[4] = 2.7; // último elemento (Length - 1) // Leer una posición double primera = notas[0]; // 4.5 double ultima = notas[4]; // 2.7 // Leer cuántos elementos tiene int cuantos = notas.Length; // 5 // ¡CUIDADO! Esto lanza IndexOutOfRangeException: // double error = notas[5];

Regla de oro: los índices válidos siempre son 0 hasta notas.Length - 1. En un arreglo de 5 elementos: posiciones 0, 1, 2, 3 y 4. Nunca se usa el índice 5.

Recorrer arreglos: for con índice vs foreach

for con índice: control total

// Accedo al índice i en cada iteración for (int i = 0; i < notas.Length; i++) { Console.WriteLine($"Nota #{i + 1}: {notas[i]}"); // Puedo modificar el arreglo: if (notas[i] < 0) notas[i] = 0; // Puedo acceder a elementos vecinos: // notas[i - 1], notas[i + 1] }

Úsalo cuando necesitas el índice, modificar elementos o comparar posiciones adyacentes.

foreach: legibilidad sin índice

// La variable 'nota' toma el valor de cada elemento foreach (double nota in notas) { Console.WriteLine($"Nota: {nota}"); // foreach NO puede modificar el arreglo: // nota = 0; ← Error de compilación } // Equivalente más legible para solo leer: double suma = 0; foreach (double nota in notas) suma += nota;

Úsalo cuando solo lees los datos y no necesitas saber la posición. El código queda más limpio.

¿Qué necesito?for con índiceforeach
Solo leer todos los elementos✓ (preferido)
Modificar elementos del arreglo
Acceder al número de posición (i)
Recorrer en orden inverso
Versión 2: solución iterativa usando arreglos

Objetivo del rediseño

  • Almacenar todas las notas en un arreglo double[] para permitir múltiples recorridos.
  • Separar la fase de carga de datos de la fase de procesamiento estadístico.
  • Facilitar extensiones futuras: ordenamientos, filtros, reportes.

Variables adicionales

  • double[] notas; para contener las N calificaciones.
  • Los contadores y acumuladores se pueden recalcular cada vez que se recorra el arreglo.
  • Es posible crear funciones reutilizables que reciban el arreglo y devuelvan métricas.
Implementación con arreglo y dos fases

Carga de datos + procesamiento separado

Console.Write("¿Cuántas notas va a ingresar? "); int cantidadNotas = int.Parse(Console.ReadLine()); double[] notas = new double[cantidadNotas]; // Fase 1: carga y validación for (int i = 0; i < cantidadNotas; i++) { Console.Write($"Nota #{i + 1}: "); double nota = double.Parse(Console.ReadLine()); if (nota < 0.0 || nota > 5.0) { Console.WriteLine("Valor inválido, la nota debe estar entre 0.0 y 5.0."); i--; // Repetir esta posición continue; } notas[i] = nota; } // Fase 2: procesamiento estadístico double suma = 0.0; double maxNota = double.MinValue; double minNota = double.MaxValue; int aprobados = 0; for (int i = 0; i < notas.Length; i++) { double nota = notas[i]; suma += nota; if (nota >= 3.0) aprobados++; if (nota > maxNota) maxNota = nota; if (nota < minNota) minNota = nota; } double promedio = suma / notas.Length; Console.WriteLine($"\nPromedio: {promedio:F2}"); Console.WriteLine($"Nota más alta: {maxNota:F2}"); Console.WriteLine($"Nota más baja: {minNota:F2}"); Console.WriteLine($"Aprobados: {aprobados}");

Separar fases hace más sencilla la lectura, depuración y posterior modularización en funciones de esta lógica.

Análisis de índices y errores frecuentes

Errores típicos

  • Usar condición incorrecta en el for: i <= notas.Length en lugar de i < notas.Length.
  • Desfase conceptual: hablar del “estudiante 1” mientras se usa índice 0 sin ajustar en la salida.
  • Reutilizar variables acumuladoras sin reinicializarlas entre diferentes recorridos del arreglo.

Buenas prácticas

  • Usar siempre i < arreglo.Length como patrón mental por defecto.
  • Mapear claramente índice interno (0-based) y número visible para humanos (1-based) en los mensajes.
  • Encapsular recorridos en funciones que reciban el arreglo y devuelvan una métrica específica.
Conexión explícita con Unidad 2 · Actividad 3

Qué exige la actividad

La tarea “Resolviendo un problema con ciclos mediante una solución iterativa en C#” pide diseñar y programar una solución que use ciclos, contadores, acumuladores y (en la versión extendida) estructuras de datos para procesar entradas múltiples. [file:54]

  • Debes demostrar dominio de la elección del ciclo adecuado según el problema.
  • Debes evidenciar el uso consciente de variables especiales (contador, acumulador, extremos, banderas).

Criterios de calidad

  • Claridad algorítmica: cada bloque de código responde a una decisión de diseño justificable.
  • Ausencia de ciclos infinitos involuntarios y errores de índice fuera de rango.
  • Capacidad de explicar en lenguaje natural qué hace cada ciclo y por qué está estructurado de esa forma.
Reto guiado de nivel avanzado

Extender el problema a nuevas preguntas de negocio

Se propone, en equipos pequeños, extender la solución con arreglos para responder al menos dos de estas preguntas:

  • ¿Cuántos estudiantes están en la franja [4.0, 5.0]? ¿Qué porcentaje representan?
  • ¿Cuál es la diferencia entre la nota más alta y el promedio? ¿Y entre el promedio y la nota más baja?
  • Si se exige una nota mínima de 3.5 para cierta beca, ¿qué estudiantes cumplen el criterio?

El énfasis no está en la “cantidad de código”, sino en la solidez del razonamiento iterativo y en la capacidad de traducir requisitos en ciclos bien definidos.

Cierre conceptual y mirada adelante

Qué debe quedar perfectamente sólido

  • La relación entre el tipo de problema, la elección del ciclo y el diseño de contadores y acumuladores.
  • La diferencia entre soluciones que solo calculan métricas sobre la marcha y soluciones que almacenan datos para posteriores análisis.
  • El rol de los arreglos como puente hacia estructuras más sofisticadas (List<T>) y hacia la programación modular de la Unidad 3. [file:54]
  • La disciplina de pensar primero el algoritmo y después la sintaxis, documentando cada decisión de diseño como si se escribiera para otra persona experta.