Sie sind auf Seite 1von 59

ALGORITMOS Y

PROGRAMACIÓN
Clase 7
Recursión

Dr. Lic. Waldo Hasperué


Temario

• Recursión

• Función recursiva

• Pila de ejecución
Recursión
• Es una técnica que permite definir problemas en términos
de si mismo

• La recursión es una técnica de programación muy


poderosa, en la cual una función realiza llamadas a
misma en pos de resolver un problema.

• Razones para su uso:


• Problemas “casi” irresolubles con las estructuras iterativas.
• Soluciones elegantes.
• Soluciones más simples.
Recursión
• En las funciones recursivas bien definidas se identificar
dos elementos:

• Caso Recursivo: aquí la función realiza algunas


operaciones con las que se reduce la complejidad del
problema y luego realiza un llamado a si misma.

• Caso Base: Se da cuando el calculo es tan simple que se


puede resolver directamente sin necesidad de hacer una
llamada recursiva.
Recursión - Ejemplo
• Definir una función recursiva que reciba un número
entero y devuelva la suma desde 1 hasta dicho número.

int sumar(int n)  return 1+2+3+….+n


Recursión - Ejemplo
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}
Recursión - Ejemplo
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

Caso recursivo:
La función se invoca a si
misma para poder devolver
un resultado.
Recursión - Ejemplo
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

Caso base:
La función finaliza con las
llamadas recursivas.
Recursión - Ejemplo
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

La ejecución del caso base o


el caso recursivo depende de
una condición.
Recursión - Ejemplo
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}
Por lo general, las funciones
recursivas devuelven un valor.
En esos casos debe existir un
return tanto en el caso base
como en el caso recursivo
Pila de ejecución
• Una función recursiva se invoca como a
cualquier otra.

int resultado = sumar(5);

• ¿Qué sucede cuando se ejecuta una función


recursiva?
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

En nuestro ejemplo n vale 5.


Al ejecutar la función se
reserva memoria para la
ejecución de la función y
poder almacenar esa variable
n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

Con n == 5 se ejecuta el caso


recursivo.
¿Qué tiene que hacer la
función para obtener el
resultado a devolver?
n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

La función se invoca a si
misma con el valor 4.

n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

Al ejecutar la función por segunda vez se


reserva memoria para almacenar la variable n.
Notar que NO es la misma posición de
memoria, hay dos posiciones de memoria n=4
distintas para la variable n.
Cada llamada de la función tiene su propio
ámbito donde almacena sus variables. n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

Nuevamente la función se
n=4
invoca a si misma

n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

n=3

n=4
Una nueva reserva de
memoria
n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
}

n=3

Nuevamente la función se
n=4
invoca a si misma

n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
} n=2

n=3

n=4
Una nueva reserva de
memoria
n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
return n + sumar(n-1);
} n=2

n=3

Nuevamente la función se
n=4
invoca a si misma

n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
n=1
return n + sumar(n-1);
} n=2

n=3

n=4
Una nueva reserva de
memoria
n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
n=1
return n + sumar(n-1);
} n=2

Esto es lo que se conoce como n=3


pila de ejecución.
Reservas de memoria
consecutivas donde se almacena n=4
el estado de la función en cada
una de sus ejecuciones.
n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
n=1
return n + sumar(n-1);
} n=2

n=3

n=4
¿Qué sucede cuando n vale 1?

n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else
n=1
return n + sumar(n-1);
} n=2

n=3

n=4
Se ejecuta el caso base.
Finaliza las llamadas recursivas
n=5
Al finalizar se libera la memoria
Pila de ejecución reservada para su ejecución.

int sumar (int n){ n=1

if (n == 1)
return n; 1
else
return n + sumar(n-1);
} n=2

n=3

En este caso la función devuelve el


valor 1. Este valor lo recibe el n=4
estado de la función sumar que hizo
la llamada.
n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n; 1
else
return n + sumar(n-1);
} n=2

n=3

En este punto se recibe el valor 1. Por lo


tanto, sabiendo el valor devuelto por la n=4
función sumar, ya puede realizar la operación
y devolver el resultado de la suma
n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n; 1
else
return n + sumar(n-1);
} n=2

n=3

¿Qué valor devuelve? n=4

n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n; 1
else
return n + sumar(n-1);
} n=2

n=3

En este estado n vale 2, se le


n=4
suma 1 (el valor recibido como
resultado) y se devuelva la suma
2+1=3 n=5
Pila de ejecución
1
int sumar (int n){
if (n == 1)
return n; n=2

else
return n + sumar(n-1); 3
}

n=3

El resultado de esta suma lo n=4


recibe el estado anterior de la
función.
n=5
Pila de ejecución
int sumar (int n){
if (n == 1) 3

return n;
else
return n + sumar(n-1); n=3

}
6

En este estado n vale 3, se le


n=4
suma 3 (el valor recibido como
resultado) y se devuelve la suma
3+3=6 n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else 6

return n + sumar(n-1);
}
n=4

10
En este estado n vale 4, se le
suma 6 (el valor recibido como
resultado) y se devuelve la suma
4 + 6 = 10 n=5
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else 10

return n + sumar(n-1);
}
n=5

15
En este estado n vale 5, se le
suma 10 (el valor recibido como
resultado) y se devuelve la suma
5 + 10 = 15
Pila de ejecución
int sumar (int n){
if (n == 1)
return n;
else 10

return n + sumar(n-1);
}
n=5

Este estado es el último en 15


ejecutarse. Notar que fue el
primero en ejecutarse. El valor de
retorno se devuelve a donde se
hizo la primer llamada.
Pila de ejecución
Llamada original a la
función sumar

int resultado = sumar(5);

El valor recibido (15) se


almacena en la variable
"resultado".
Pila de ejecución

¿ Qué sucede con esta llamada ?

int resultado = sumar (-1);


Pila de ejecución
-1
int sumar (int n){ -2
-3
if (n == 1) -4
return n; -5

else
return n + sumar(n-1);
}

• Si una función recursiva no alcanza nunca el caso base,


seguirá haciendo llamadas recursivas para siempre y
nunca terminaría. Esta circunstancia se conoce como
recursión infinita.
Pila de ejecución
• Lo anterior en realidad no ocurre nunca ya que el
verdadero problema de la recursión es la cantidad
máxima de memoria con la cual cuenta un programa para
su ejecución.

• Si una función recursiva hace muchas llamadas, cada


llamada reserva memoria y esta podría acabarse.

• Si cada estado de una llamada recursiva debe almacenar


muchos datos, la memoria podría acabarse con pocas
llamadas.
Pila de ejecución
• Cuando la memoria se llena .NET levanta una excepción
llamada StackOverflowException
Recursión
• Es muy común implementar funciones recursivas que
trabajen sobre colecciones de datos: strings, arreglos,
ArrayList, Pilas, Colas.

• En estos casos se trabaja siempre sobre la misma idea,


resolver solo una parte del problema con un elemento y el
resto del problema (los elementos restantes) se resuelven
de manera recursiva:
• En el caso recursivo se debe sacar un elemento de la colección,
para tratar el resto de la colección de manera recursiva
• Por lo general el caso base se da cuando la colección queda vacía
o con un único elemento.
Recursión – Ejemplo
• Implementar una función recursiva que reciba un string y
retorne el string inverso. Por ejemplo: si se ingresa
"RECURSION" la función devuelve "NOISRUCER"

• Los elementos de la colección en este caso son


caracteres.

• La inversión del string debería tomar un caracter y


concatenarlo al string restante (invertido). Esta inversión
se resuelve de manera recursiva.
Recursión – Ejemplo
R E C U R S I O N
Recursión – Ejemplo
R E C U R S I O N

El primer carácter debería ubicarse en


Estado 1
la última posición

LetraMovida = 'R'
Resto = "ECURSION"

R
Recursión – Ejemplo
R E C U R S I O N

Estado 1 El resto del string se invierte de manera


recursiva.

LetraMovida = 'R'
Resto = "ECURSION"

R
Recursión – Ejemplo
R E C U R S I O N

El primer carácter debería ubicarse en


Estado 2
la última posición

LetraMovida = 'E'
Resto = "CURSION"

E R
Recursión – Ejemplo
R E C U R S I O N

Estado 2 El resto del string se invierte de manera


recursiva.

LetraMovida = 'E'
Resto = "CURSION"

E R
Recursión – Ejemplo
R E C U R S I O N

El primer carácter debería ubicarse en


Estado 3
la última posición

LetraMovida = 'C'
Resto = "URSION"

C E R
Recursión – Ejemplo
R E C U R S I O N

Estado 3 El resto del string se invierte de manera


recursiva.

LetraMovida = 'C'
Resto = "URSION"

C E R
Recursión – Ejemplo
R E C U R S I O N

Estado 9 Siguiendo con este mecanismo se


continua invirtiendo de manera recursiva
el resto del string hasta llegar al string de
LetraMovida = 'N' longitud 0.
Resto = ""

N O I S R U C E R
Recursión – Ejemplo
R E C U R S I O N

Estado 10
El caso base (string vacío) devuelve el
propio string vacío.
Caso base

N O I S R U C E R
Recursión – Ejemplo
R E C U R S I O N

Estado 9
Esta llamada concatena el string recibido
con la letra que estaba moviendo.
LetraMovida = 'N'
Resto = ""
StringRecibido = ""

N O I S R U C E R
Recursión – Ejemplo
R E C U R S I O N

Estado 8
Esta llamada concatena el string recibido
con la letra que estaba moviendo.
LetraMovida = 'O'
Resto = "N"
StringRecibido = "N"

N O I S R U C E R
Recursión – Ejemplo
R E C U R S I O N

Estado 8
Esta llamada concatena el string recibido
con la letra que estaba moviendo.
LetraMovida = 'I'
Resto = "ON"
StringRecibido = "NO"

N O I S R U C E R
Recursión – Ejemplo
R E C U R S I O N

Estado 1

Así continúa hasta volver al estado 1 que


LetraMovida = 'R' termina de invertir el string completo
Resto = "ECURSION"
StringRecibido =
"NOISRUCE"

N O I S R U C E R
Recursión – Ejemplo
string invertir(string cadena){
if (cadena == "")
return "";
else {
char letra = cadena[0];
string resto = cadena.Substring(1);
return invertir(resto) + letra;
}
}
Recursión – Ejemplo
string invertir(string cadena){
if (cadena == "") Caso base: string
return ""; vacío

else {
char letra = cadena[0];
string resto = cadena.Substring(1);
return invertir(resto) + letra;
}
}
Recursión – Ejemplo
string invertir(string cadena){
Guardamos la
if (cadena == "") primer letra
return "";
else {
char letra = cadena[0];
string resto = cadena.Substring(1);
return invertir(resto) + letra;
}
}
Recursión – Ejemplo
string invertir(string cadena){
Obtenemos el
if (cadena == "") resto del string
return "";
else {
char letra = cadena[0];
string resto = cadena.Substring(1);
return invertir(resto) + letra;
}
}
Recursión – Ejemplo
string invertir(string cadena){
if (cadena == "")
return "";
else {
char letra = cadena[0];
string resto = cadena.Substring(1);
return invertir(resto) + letra;
}
} El string resto se invierte
de forma recursiva, el
string invertido se
concatena con la primer
letra poniendo a ésta al
final del string.
Recursión
• Ventajas
• Soluciones simples y claras
• Soluciones elegantes
• Soluciones a problemas complejos

• Desventajas
• Manejo ineficiente de memoria
• Una misma solución podría calcularse más de una vez (serie de
Fibonacci)

Das könnte Ihnen auch gefallen