Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Ejercicios de Recursividad

Universidad Nacional de Río Negro

Prerrequisitos y Entorno de Ejecución Requerido

Para compilar y verificar las soluciones de este módulo bajo el estándar C11 estricto de cátedra, se requiere:

Objetivos Pedagógicos y Competencias (Taxonomía de Bloom)

Capítulos de Apunte Correspondientes


Matemática Recursiva

Ejercicio 3.07.01 - Factorial ⭐⭐☆☆☆

```c #include <stdio.h> #include <assert.h> unsigned long long factorial_rec(unsigned int n) { if (n <= 1) { return 1ULL; } return (unsigned long long)n * factorial_rec(n - 1); } int main(void) { assert(factorial_rec(0) == 1ULL); assert(factorial_rec(1) == 1ULL); assert(factorial_rec(2) == 2ULL); assert(factorial_rec(5) == 120ULL); assert(factorial_rec(10) == 3628800ULL); return 0; } ```

(ej_b3_c07_02)=
### Ejercicio 3.07.02 - Suma Recursiva ⭐⭐☆☆☆

:::{exercise}
:label: ej_b3_c07_02_suma

Implementá la suma de dos enteros no negativos $a + b$ decrementando el segundo
operando hasta converger al caso base $b = 0$.

```c
unsigned int suma_rec(unsigned int a, unsigned int b);
```

**Tabla de Vectores de Prueba:**

| Operandos (`a`, `b`) | Salida Esperada |
| :--- | :--- |
| `a = 5, b = 0` | `5` |
| `a = 0, b = 7` | `7` |
| `a = 12, b = 8` | `20` |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>

unsigned int suma_rec(unsigned int a, unsigned int b) {
    if (b == 0) {
        return a;
    }
    return suma_rec(a + 1, b - 1);
}

int main(void) {
    assert(suma_rec(5, 0) == 5);
    assert(suma_rec(0, 7) == 7);
    assert(suma_rec(12, 8) == 20);
    assert(suma_rec(0, 0) == 0);
    return 0;
}
```

(ej_b3_c07_03)=
### Ejercicio 3.07.03 - Producto por Sumas Sucesivas ⭐⭐☆☆☆

:::{exercise}
:label: ej_b3_c07_03_producto

Implementá la multiplicación recursiva de dos enteros no negativos $a \times b$
mediante acumulaciones sucesivas.

```c
unsigned int producto_rec(unsigned int a, unsigned int b);
```

**Tabla de Vectores de Prueba:**

| Operandos (`a`, `b`) | Salida Esperada |
| :--- | :--- |
| `a = 4, b = 0` | `0` |
| `a = 6, b = 1` | `6` |
| `a = 7, b = 5` | `35` |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>

unsigned int producto_rec(unsigned int a, unsigned int b) {
    if (b == 0) {
        return 0;
    }
    return a + producto_rec(a, b - 1);
}

int main(void) {
    assert(producto_rec(4, 0) == 0);
    assert(producto_rec(0, 9) == 0);
    assert(producto_rec(6, 1) == 6);
    assert(producto_rec(7, 5) == 35);
    return 0;
}
```

(ej_b3_c07_04)=
### Ejercicio 3.07.04 - Potencia Entera ⭐⭐☆☆☆

:::{exercise}
:label: ej_b3_c07_04_potencia

Calculá $base^{exp}$ para un exponente entero no negativo de manera recursiva.

```c
long long potencia_rec(long long base, unsigned int exp);
```

**Tabla de Vectores de Prueba:**

| Entrada (`base`, `exp`) | Salida Esperada |
| :--- | :--- |
| `base = 5, exp = 0` | `1LL` |
| `base = 2, exp = 8` | `256LL` |
| `base = -3, exp = 3` | `-27LL` |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>

long long potencia_rec(long long base, unsigned int exp) {
    if (exp == 0) {
        return 1LL;
    }
    return base * potencia_rec(base, exp - 1);
}

int main(void) {
    assert(potencia_rec(5, 0) == 1LL);
    assert(potencia_rec(2, 8) == 256LL);
    assert(potencia_rec(-3, 3) == -27LL);
    assert(potencia_rec(-3, 2) == 9LL);
    return 0;
}
```

---

## Series Recursivas

(ej_b3_c07_05)=
### Ejercicio 3.07.05 - Fibonacci ⭐⭐☆☆☆

:::{exercise}
:label: ej_b3_c07_05_fibonacci

Calculá el n-ésimo término de Fibonacci respetando:
$F(0) = 0$, $F(1) = 1$, $F(n) = F(n-1) + F(n-2)$.

```c
unsigned long long fibonacci_rec(unsigned int n);
```

**Tabla de Vectores de Prueba:**

| Índice `n` | Fibonacci Esperado |
| :--- | :--- |
| `0` | `0ULL` |
| `1` | `1ULL` |
| `6` | `8ULL` |
| `10` | `55ULL` |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>

unsigned long long fibonacci_rec(unsigned int n) {
    if (n == 0) {
        return 0ULL;
    }
    if (n == 1) {
        return 1ULL;
    }
    return fibonacci_rec(n - 1) + fibonacci_rec(n - 2);
}

int main(void) {
    assert(fibonacci_rec(0) == 0ULL);
    assert(fibonacci_rec(1) == 1ULL);
    assert(fibonacci_rec(2) == 1ULL);
    assert(fibonacci_rec(6) == 8ULL);
    assert(fibonacci_rec(10) == 55ULL);
    return 0;
}
```

---

## Arreglos Recursivos

(ej_b3_c07_06)=
### Ejercicio 3.07.06 - Copiar Arreglo Recursivo ⭐⭐⭐☆☆

:::{exercise}
:label: ej_b3_c07_06_copiar_arr

Copiá los elementos de un arreglo a un búfer destino en el mismo orden usando
recursión y aritmética de punteros.

```c
void copiar_arreglo_rec(const int *origen, int *destino, size_t n);
```

**Tabla de Vectores de Prueba:**

| Arreglo Origen | `n` | Arreglo Destino Resultante |
| :--- | :--- | :--- |
| `[10, 20, 30]` | `3` | `[10, 20, 30]` |
| `[]` | `0` | Sin modificaciones |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>
#include <stddef.h>

void copiar_arreglo_rec(const int *origen, int *destino, size_t n) {
    if (n == 0) {
        return;
    }
    *destino = *origen;
    copiar_arreglo_rec(origen + 1, destino + 1, n - 1);
}

int main(void) {
    int orig[] = {10, 20, 30, 40};
    int dest[4] = {0};

    copiar_arreglo_rec(orig, dest, 4);
    assert(dest[0] == 10 && dest[1] == 20 && dest[2] == 30 && dest[3] == 40);

    /* Caso de longitud cero */
    int vacio[2] = {99, 99};
    copiar_arreglo_rec(orig, vacio, 0);
    assert(vacio[0] == 99 && vacio[1] == 99);

    return 0;
}
```

(ej_b3_c07_07)=
### Ejercicio 3.07.07 - Invertir Arreglo Recursivo ⭐⭐⭐☆☆

:::{exercise}
:label: ej_b3_c07_07_invertir_arr

Copiá los elementos de un arreglo a un búfer destino en orden inverso usando recursión.

```c
void invertir_arreglo_rec(const int *origen, int *destino, size_t n);
```

**Tabla de Vectores de Prueba:**

| Entrada | `n` | Salida Invertida |
| :--- | :--- | :--- |
| `[1, 2, 3, 4]` | `4` | `[4, 3, 2, 1]` |
| `[42]` | `1` | `[42]` |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>
#include <stddef.h>

void invertir_arreglo_rec(const int *origen, int *destino, size_t n) {
    if (n == 0) {
        return;
    }
    destino[n - 1] = *origen;
    invertir_arreglo_rec(origen + 1, destino, n - 1);
}

int main(void) {
    int orig[] = {1, 2, 3, 4};
    int dest[4] = {0};

    invertir_arreglo_rec(orig, dest, 4);
    assert(dest[0] == 4 && dest[1] == 3 && dest[2] == 2 && dest[3] == 1);

    int uno[] = {42};
    int dest_uno[1] = {0};
    invertir_arreglo_rec(uno, dest_uno, 1);
    assert(dest_uno[0] == 42);

    return 0;
}
```

(ej_b3_c07_08)=
### Ejercicio 3.07.08 - Suma de Elementos de un Arreglo ⭐⭐☆☆☆

:::{exercise}
:label: ej_b3_c07_08_suma_arr

Sumá todos los enteros de un arreglo de forma recursiva.

```c
long long suma_arreglo_rec(const int *arr, size_t n);
```

**Tabla de Vectores de Prueba:**

| Arreglo | `n` | Suma Esperada |
| :--- | :--- | :--- |
| `[1, 2, 3, 4, 5]` | `5` | `15LL` |
| `[-5, 10, -3]` | `3` | `2LL` |
| `[]` | `0` | `0LL` |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>
#include <stddef.h>

long long suma_arreglo_rec(const int *arr, size_t n) {
    if (n == 0) {
        return 0LL;
    }
    return (long long)*arr + suma_arreglo_rec(arr + 1, n - 1);
}

int main(void) {
    int a1[] = {1, 2, 3, 4, 5};
    assert(suma_arreglo_rec(a1, 5) == 15LL);

    int a2[] = {-5, 10, -3};
    assert(suma_arreglo_rec(a2, 3) == 2LL);

    assert(suma_arreglo_rec(a1, 0) == 0LL);
    return 0;
}
```

---

## Cadenas Recursivas

(ej_b3_c07_09)=
### Ejercicio 3.07.09 - Palíndromo ⭐⭐☆☆☆

:::{exercise}
:label: ej_b3_c07_09_palindromo

Determiná si una secuencia de caracteres es un palíndromo comprobando los
extremos y llamando recursivamente al segmento interno.

```c
bool es_palindromo_rec(const char *inicio, const char *fin);
```

**Tabla de Vectores de Prueba:**

| Cadena | `inicio` / `fin` | Resultado Esperado |
| :--- | :--- | :--- |
| `"neuquen"` | Punteros a `'n'` y `'n'` | `true` |
| `"radar"` | Punteros a `'r'` y `'r'` | `true` |
| `"caterpillar"` | Punteros a `'c'` y `'r'` | `false` |
| `""` | `inicio > fin` | `true` |

::::{solution}
```c
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>

bool es_palindromo_rec(const char *inicio, const char *fin) {
    if (inicio >= fin) {
        return true;
    }
    if (*inicio != *fin) {
        return false;
    }
    return es_palindromo_rec(inicio + 1, fin - 1);
}

static bool verificar_palindromo(const char *str) {
    size_t len = strlen(str);
    if (len == 0) {
        return true;
    }
    return es_palindromo_rec(str, str + len - 1);
}

int main(void) {
    assert(verificar_palindromo(""));
    assert(verificar_palindromo("a"));
    assert(verificar_palindromo("neuquen"));
    assert(verificar_palindromo("radar"));
    assert(!verificar_palindromo("caterpillar"));
    assert(!verificar_palindromo("hola"));
    return 0;
}
```

(ej_b3_c07_10)=
## Ejercicio 3.07.10 - Factorial Recursivo ⭐☆☆☆☆

Implementá factorial de forma recursiva.

**Orientación:**
- Caso base: `n <= 1` retorna 1
- Caso recursivo: `n * factorial(n-1)`
- Limitación: rápido overflow para n grande

---

(ej_b3_c07_11)=
## Ejercicio 3.07.11 - Potencia Recursiva ⭐⭐☆☆☆

Calculá base^exponente recursivamente.

**Orientación:**
- Caso base: `exp == 0` retorna 1
- Caso recursivo: `base * potencia(base, exp-1)`
- Versión mejorada (siguiente ejercicio)

---

(ej_b3_c07_12)=
### Ejercicio 3.07.12 - Potencia Rápida por Divide y Vencerás ⭐⭐⭐☆☆

:::{exercise}
:label: ej_b3_c07_12_potencia_rapida

Optimizá el cómputo de la potencia entera aplicando el paradigma de divide y conquista:
- Si el exponente `exp == 0`, el resultado es `1ULL`.
- Si `exp` es par: `potencia_rapida(base, exp) = potencia_rapida(base * base, exp / 2)`.
- Si `exp` es impar: `potencia_rapida(base, exp) = base * potencia_rapida(base, exp - 1)`.

```c
unsigned long long potencia_rapida_rec(unsigned long long base, unsigned int exp);
```

La complejidad temporal resultante es $O(\log_2 \text{exp})$ en lugar de $O(\text{exp})$.

**Tabla de Vectores de Prueba:**

| Base | Exponente | Salida Esperada | Justificación |
| :--- | :--- | :--- | :--- |
| `2` | `0` | `1ULL` | Caso base exponente 0 |
| `2` | `10` | `1024ULL` | $2^{10} = 1024$ |
| `3` | `5` | `243ULL` | $3^5 = 243$ |
| `10` | `4` | `10000ULL` | $10^4 = 10000$ |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>

unsigned long long potencia_rapida_rec(unsigned long long base, unsigned int exp) {
    if (exp == 0) {
        return 1ULL;
    }
    if (exp % 2 == 0) {
        return potencia_rapida_rec(base * base, exp / 2);
    }
    return base * potencia_rapida_rec(base, exp - 1);
}

int main(void) {
    assert(potencia_rapida_rec(2, 0) == 1ULL);
    assert(potencia_rapida_rec(5, 1) == 5ULL);
    assert(potencia_rapida_rec(2, 10) == 1024ULL);
    assert(potencia_rapida_rec(3, 5) == 243ULL);
    assert(potencia_rapida_rec(10, 4) == 10000ULL);
    return 0;
}
```

(ej_b3_c07_13)=
### Ejercicio 3.07.13 - Suma Recursiva de Dígitos ⭐⭐☆☆☆

:::{exercise}
:label: ej_b3_c07_13_suma_digitos

Implementá una función pura que sume los dígitos de un entero no negativo de forma recursiva:
- Caso base: si $n < 10$, retorna $n$.
- Paso recursivo: $(n \pmod{10}) + \text{suma\_digitos\_rec}(n / 10)$.

```c
unsigned int suma_digitos_rec(unsigned long long n);
```

**Tabla de Vectores de Prueba:**

| Entrada $n$ | Descomposición | Retorno Esperado |
| :--- | :--- | :--- |
| `0` | Caso base | `0` |
| `7` | Caso base | `7` |
| `12345` | $5 + 4 + 3 + 2 + 1$ | `15` |
| `999` | $9 + 9 + 9$ | `27` |

::::{solution}
```c
#include <stdio.h>
#include <assert.h>

unsigned int suma_digitos_rec(unsigned long long n) {
    if (n < 10ULL) {
        return (unsigned int)n;
    }
    return (unsigned int)(n % 10ULL) + suma_digitos_rec(n / 10ULL);
}

int main(void) {
    assert(suma_digitos_rec(0) == 0);
    assert(suma_digitos_rec(7) == 7);
    assert(suma_digitos_rec(12345) == 15);
    assert(suma_digitos_rec(999) == 27);
    return 0;
}
```

(ej_b3_c07_14)=
### Ejercicio 3.07.14 - Torres de Hanoi con Registro de Movimientos ⭐⭐⭐☆☆

:::{exercise}
:label: ej_b3_c07_14_hanoi
:enumerator: recursion-14

El clásico problema de las Torres de Hanoi ilustra la descomposición canónica de un problema en tres etapas recursivas:
1. Mover $n-1$ discos desde la aguja `origen` hasta la aguja `auxiliar` utilizando `destino` como pivote.
2. Mover el disco restante $n$ desde `origen` hasta `destino`.
3. Mover los $n-1$ discos desde `auxiliar` hasta `destino` utilizando `origen` como pivote.

Definí la estructura:
```c
typedef struct {
    int disco;
    char origen;
    char destino;
} movimiento_hanoi_t;
```

Implementá la función recursiva:
```c
size_t hanoi_registrar(int n, char origen, char destino, char auxiliar, movimiento_hanoi_t *registro, size_t cap_max, size_t *indice_actual);
```

- **Invariante y Complejidad:** La cantidad exacta de movimientos mínimos para $n$ discos es siempre $2^n - 1$.
- Si `registro != NULL`, guarda cada movimiento secuencialmente si `*indice_actual < cap_max`.
- Retorna el conteo total acumulado de movimientos efectuados.

#### Tabla de Vectores de Prueba Obligatorios

| Discos $n$ | Agujas (Orig, Dest, Aux) | Movimientos Esperados ($2^n - 1$) | Primer Movimiento | Último Movimiento |
| :--- | :--- | :--- | :--- | :--- |
| `1` | `'A', 'C', 'B'` | `1` | Disco 1: A $\to$ C | Disco 1: A $\to$ C |
| `2` | `'A', 'C', 'B'` | `3` | Disco 1: A $\to$ B | Disco 1: B $\to$ C |
| `3` | `'A', 'C', 'B'` | `7` | Disco 1: A $\to$ C | Disco 1: A $\to$ C |
| `4` | `'A', 'C', 'B'` | `15` | Disco 1: A $\to$ B | Disco 1: B $\to$ C |

Ejercicio 3.07.15 - Fibonacci Recursivo ⭐⭐⭐☆☆

Calculá el n-ésimo número de Fibonacci.

Orientación:


Ejercicio 3.07.16 - Fibonacci con Memoización ⭐⭐⭐⭐☆

Optimizá Fibonacci usando memoización (caché de resultados).

Orientación:


Ejercicio 3.07.17 - Suma de Array ⭐⭐☆☆☆

Sumá elementos de un array recursivamente.

Orientación:


Ejercicio 3.07.18 - Búsqueda Binaria Recursiva ⭐⭐⭐☆☆

Implementá búsqueda binaria de forma recursiva.

Orientación:


Ejercicio 3.07.19 - Máximo en Array ⭐⭐⭐☆☆

Encontrá el máximo de un array recursivamente.

Orientación:


Ejercicio 3.07.20 - Palíndromo Recursivo ⭐⭐⭐☆☆

Verificá si un string es palíndromo recursivamente.

Orientación:


Ejercicio 3.07.21 - Torre de Hanoi ⭐⭐⭐⭐☆

Resolvé el problema de la Torre de Hanoi.

Reglas:

Orientación:

1
2
3
4
5
6
7
8
9
10
11
12
13
void hanoi(int n, char origen, char destino, char auxiliar)
{
    if (n == 1)
    {
        mover(origen, destino);
    }
    else
    {
        hanoi(n - 1, origen, auxiliar, destino);
        mover(origen, destino);
        hanoi(n - 1, auxiliar, destino, origen);
    }
}

Ejercicio 3.07.22 - Permutaciones ⭐⭐⭐⭐⭐

Generá todas las permutaciones de un string.

Orientación:


Ejercicio 3.07.23 - Subconjuntos (Power Set) ⭐⭐⭐⭐⭐

Generá todos los subconjuntos de un conjunto.

Orientación:


Ejercicio 3.07.24 - MergeSort ⭐⭐⭐⭐⭐

Implementá ordenamiento MergeSort.

Algoritmo:

  1. Dividir array en dos mitades

  2. Ordenar recursivamente cada mitad

  3. Fusionar (merge) las mitades ordenadas

Orientación:


Ejercicio 3.07.25 - QuickSort ⭐⭐⭐⭐⭐

Implementá ordenamiento QuickSort.

Algoritmo:

  1. Elegir pivote

  2. Particionar: menores a izquierda, mayores a derecha

  3. Ordenar recursivamente cada partición

Orientación:


Ejercicio 3.07.26 - N Reinas ⭐⭐⭐⭐⭐

Resolvé el problema de las N reinas (colocar N reinas en tablero N×N sin que se ataquen).

Orientación:


Ejercicio 3.07.27 - Laberinto (Path Finding) ⭐⭐⭐⭐⭐

Encontrá camino en un laberinto desde inicio a fin.

Orientación:


Ejercicio 3.07.28 - Combinaciones (n choose k) ⭐⭐⭐⭐⭐

Generá todas las combinaciones de k elementos de un conjunto de n.

Orientación:


Ejercicio 3.07.29 - Sudoku Solver ⭐⭐⭐⭐⭐

Resolvé un Sudoku usando backtracking.

Orientación:


Ejercicio 3.07.30 - s Adicionales ⭐⭐☆☆☆

Ejercicio 3.07.31 - Coeficiente Binomial ⭐⭐⭐☆☆

Calculá C(n,k) = n! / (k! × (n-k)!) recursivamente.

Orientación:


Ejercicio 3.07.32 - GCD (MCD) Recursivo ⭐⭐☆☆☆

Implementá algoritmo de Euclides recursivamente.

Orientación:


Ejercicio 3.07.33 - Árbol de Expresión ⭐⭐⭐⭐⭐

Evaluá una expresión matemática representada como árbol binario.

Orientación:


Notas Finales

Estas consignas cubren desde recursión básica hasta algoritmos avanzados de backtracking y divide y conquista, desarrollando pensamiento recursivo esencial.