Appearance
1. Conteos
Proceso de contar elementos, sucesos o características específicas en un conjunto de datos. Se puede hacer de dos formas:
| Enfoque | Descripción |
|---|---|
| Iteración | Recorrer el conjunto y sumar cada vez que se cumple una condición. |
| Recursión | La función se llama a sí misma para procesar partes más pequeñas del conjunto y realizar el conteo. |
Ejemplo básico #3: conteo con recursividad
java
public static int contarElementos(List<Integer> lista, int index) {
if (index == lista.size()) {
return 0; // Caso base: llegamos al final, no hay más elementos
} else {
return 1 + contarElementos(lista, index + 1); // Sumamos 1 y seguimos con el siguiente
}
}Ejemplo básico #4: conteo con iteración
java
public static int contarElementos(List<Integer> lista) {
int count = 0;
for (int i = 0; i < lista.size(); i++) {
count++; // Aumentamos el contador por cada elemento
}
return count;
}2. Procesos recursivos de lista
- La recursividad se utiliza cuando un problema tiene una estructura compleja o no lineal (colecciones de objetos, conjuntos de datos) y no se resuelve fácilmente con bucles.
- Las listas de objetos pueden contener otros objetos o colecciones. La recursividad permite procesarlas de forma jerárquica, operando sobre cada elemento o subestructura.
- Un proceso recursivo es aquel en el que una función se llama a sí misma para procesar los elementos de una lista. Es útil cuando el problema se descompone en subproblemas similares. Aunque simplifica el código, no siempre es la mejor opción.
Ejemplo: suma recursiva de una lista
java
import java.util.List;
public class Estructuras {
// Método recursivo para sumar los elementos de la lista
public static int sumaLista(List<Integer> lista) {
// Caso base: si la lista está vacía, el resultado es 0
if (lista.isEmpty()) {
return 0;
}
// Caso recursivo: primer elemento + suma del resto de la lista
else {
return lista.get(0) + sumaLista(lista.subList(1, lista.size()));
}
}
public static void main(String[] args) {
List<Integer> miLista = List.of(1, 2, 3, 4, 5);
int resultado = sumaLista(miLista);
System.out.println(resultado); // Output: 15
}
}3. Removiendo recursividad
Aunque la recursividad puede ser elegante, en ocasiones no es la mejor opción, sobre todo si hay preocupación por el rendimiento o si la pila de llamadas puede desbordarse con listas muy grandes. En esos casos se elimina la recursividad y se usa una solución iterativa.
Ejemplo: suma iterativa
java
import java.util.List;
public class SumaIterativa {
public static int sumarLista(List<Integer> lista) {
int suma = 0;
for (int num : lista) {
suma += num; // Sumar cada elemento
}
return suma;
}
public static void main(String[] args) {
List<Integer> numeros = List.of(1, 2, 3, 4, 5);
int resultado = sumarLista(numeros); // Llamada iterativa
System.out.println("La suma de la lista es: " + resultado);
}
}Ventaja: evita la sobrecarga de la pila de llamadas (no hay llamadas recursivas) y es más eficiente en uso de memoria.
4. Decidiendo cuándo usar y cuándo no usar recursividad
Cuándo usarla
| Situación | Explicación |
|---|---|
| Estructuras jerárquicas o anidadas | Árboles, grafos, listas anidadas o cualquier estructura que se pueda dividir en subproblemas de forma recursiva. |
| Subproblemas similares | El problema se descompone en subproblemas del mismo tipo. |
| Búsqueda o clasificación | Búsqueda binaria, QuickSort y MergeSort son naturalmente recursivos. |
| Simplificación del código | En algunos casos lo hace más legible y expresivo. |
Cuándo NO usarla
| Situación | Explicación |
|---|---|
| Alto rendimiento o grandes datos | Mayor costo de memoria por la pila de llamadas; con listas grandes puede agotar la memoria. |
| Falta de caso base clara | Si no se puede definir un caso base que termine la recursión, puede generar llamadas infinitas. |
| Operaciones simples sobre colecciones | Si se resuelve fácilmente con bucles o métodos integrados, la iteración es más simple y generalmente más eficiente. |
| Complejidad y legibilidad | Si el problema no se presta a la descomposición recursiva, la solución se vuelve más compleja y menos legible. |
Aplicaciones de la recursividad
Factorial (recursivo e iterativo)
Comparar la eficiencia de ambos enfoques ayuda a entender las diferencias de rendimiento.
java
// Recursivo
public static int factorialRecursivo(int n) {
if (n == 0) {
return 1;
} else {
return n * factorialRecursivo(n - 1);
}
}
// Iterativo
public static int factorialIterativo(int n) {
int resultado = 1;
for (int i = 1; i <= n; i++) {
resultado *= i;
}
return resultado;
}Fibonacci (recursivo e iterativo)
Sucesión en la que cada número es la suma de los dos anteriores.
java
// Recursivo
public static int fibonacciRecursivo(int n) {
if (n <= 1) {
return n;
} else {
return fibonacciRecursivo(n - 1) + fibonacciRecursivo(n - 2);
}
}
// Iterativo
public static int fibonacciIterativo(int n) {
if (n <= 1) {
return n;
}
int a = 0, b = 1, c;
for (int i = 2; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return b;
}Ideas clave para recordar
- Toda función recursiva necesita un caso base y un caso recursivo que se acerque al caso base.
- La recursividad brilla en estructuras jerárquicas y problemas que se dividen en subproblemas iguales.
- Toda solución recursiva se puede convertir a iterativa; se elige según rendimiento, memoria y legibilidad.