EJEMPLO
Dada una matriz [1,2,3] o [1,2,3,4], imprimir todas las combinaciones únicas para la longitud 3.
CÓDIGO
public class PrintCombo {
public void printCombo(int [] a, int [] buffer, int startIndex, int bufferIndex){
printArray(buffer);
if(buffer.length == bufferIndex){
System.out.println();
System.out.println("SOLUTION START");
printArray(buffer);
System.out.println("SOLUTION END");
System.out.println();
return;
}
if(startIndex == a.length){
return;
}
for(int i = startIndex; i<a.length; i++){
buffer[bufferIndex] = a[i];
printCombo(a,buffer,i+1,bufferIndex+1);
}
}
public void printArray(int [] buffer){
for(int i = 0; i<buffer.length; i++){
System.out.print(" "+buffer[i]);
}
System.out.println();
}
}
SALIDA
Para array [1,2,3] ==> 1,2,3
Para array [1,2,3,4] ==> 1,2,3 || 1,2,4 || 1,3,4 || 2,3,4
Problema
He pasado 3 horas trazando el código utilizando un depurador y todavía estoy tratando de entender cómo la lógica recursiva está trabajando.
Por ejemplo, tomemos un ejemplo, cuando la matriz es [1,2,3].
- PrintCombo (a, buffer, 0, 0)
- buffer [0] se actualiza a 1
- Llamamos a PrintCombo (a, amortiguar, 1, 1)
- buffer [1] se actualiza a 2
- Llamamos a PrintCombo (a, amortiguar, 2, 2)
- buffer [2] se actualiza a 3
- Llamamos a PrintCombo (a, amortiguar, 3, 3)
- Desde buffer.length == bufferIndex que llamamos printarray.
- Volvemos a la llamada anterior
Aquí es donde me pierdo. ¿Cómo funciona la pila realizar llamadas anteriores? Estoy intentando difícil de entender este enfoque a fondo como no me gusta soluciones memorización.
Decidí modificar mi código mediante la adición de una declaración de impresión para ver lo que hay dentro de la memoria intermedia en cada iteración. Esto es lo que imprimí por ejemplo a = [1,2,3] y tampón tamaño es 3.
0 0 0
1 0 0
1 2 0
1 2 3
SOLUTION START
1 2 3
SOLUTION END
1 3 3
2 3 3
2 3 3
3 3 3
Cuando se llama inicialmente el bucle en printCombo
voluntad en cada iteración ajuste el primer elemento de buffer
a todos los valores posibles:
[1,-,-] // i = 0, first iteration
[2,-,-] // i = 1, second iteration
[3,-,-] // i = 2, ...
[4,-,-] // i = 3, ...
Para cada una de estas iteraciones hay una llamada recursiva para printCombo
crear todas las combinaciones posibles para los elementos restantes en buffer
. Por ejemplo, en la primera iteración [1,_,_]
se pasa a printCombo
, cuyo bucle for ahora fijará el segundo elemento también todos los valores posibles:
[1,2,-] // i = 0, first iteration in first recursive call to printCombo
[1,3,-] // i = 1, second iteration in first recursive call to printCombo
[1,4,_] // i = 2, ...
El proceso continúa hasta que buffer
es completa (primera if
condición) o la piscina de valores posibles se agota (segunda if
condición). En el primer caso el candidato se encuentra y se imprime. En el segundo caso se alcanza un punto muerto.
Aquí es la evolución de buffer
tiempo durante el que el nivel de indentación corresponde a la profundidad de recursión ( a = [1,2,3,4]
y el tamaño de búfer 3
):
[1,-,-]
[1,2,-]
[1,2,3] // buffer.length == bufferIndex -> printArray
[1,2,4] // buffer.length == bufferIndex -> printArray
[1,3,-]
[1,3,4] // buffer.length == bufferIndex -> printArray
[1,4,-] // startIndex == a.length -> return
[2,-,-]
[2,3,-]
[2,3,4] // buffer.length == bufferIndex -> printArray
[2,4,-] // startIndex == a.length -> return
[3,-,-]
[3,4,-] // startIndex == a.length -> return
[4,-,-] // startIndex == a.length -> return