This question is asked few times but I still find it quite difficult to convert easily readable and intuitive code into iterative code. For example I was practicing a coding question and I am given 26 integers which indicate how many times each character appears in the string. I should print all possible strings. Following is my recursive code
private static void combinatorial(String prefix, ArrayList<Integer> remainingToFill,
int totalLength) {
if (prefix.length() == totalLength) {
System.out.println(prefix);
}
for (int i = 0; i < remainingToFill.size(); i++) {
if (remainingToFill.get(i) > 0) {
ArrayList<Integer> toFill = new ArrayList<>(remainingToFill);
toFill.set(i, toFill.get(i) - 1);
combinatorial(prefix + (char) ('a' + i), toFill, totalLength);
}
}
}
I coded iterative version of this but the resultant function is much more complex and not readable and took more time for me to code it. How do I tackle this kind of problem? Is there any simple technique I can follow which would lead to easy and readable code?
Which data structure is best suited for converting recursive implementation to iterative implementation of an algorithm? Explanation: Since function calls are executed in Last In First Out order, stack is the data structure for converting recursive to iterative implementation.
Any recursive algorithm can be replaced with a non-recursive algorithm by using a Stack .
Recursive termination conditionsA recursive termination is a condition that, when met, will cause the recursive function to stop calling itself. Because of the termination condition, countDown(1) does not call countDown(0) -- instead, the “if statement” does not execute, so it prints “pop 1” and then terminates.
Strive to make your recursive call Tail Recursion (recursion where the last statement is the recursive call). Once you have that, converting it to iteration is generally pretty easy.
Even using stack will not convert a recursive algorithm into iterative. Normal recursion is function based recursion and if we use stack then it becomes stack based recursion. But its still recursion.
Note that recursion and iteration are generally equivalent; one can almost always be converted to the other. A tail-recursive function is very easily converted to an iterative one.
Recursion is nothing but the process of calling of one function from the other only this process is done by calling of a function by itself. As we know when one function calls the other function the first function saves its state (its variables) and then passes the control to the called function.
Well, the reason programming languages support recursive expression of programs is that it's often simpler than explicit iterative forms. So your question is almost self-answering.
However, there really is a methodical way to convert recursive forms to iterative forms that always works. It helps to have a language with goto
, which Java doesn't.
First let's clean up the java. We want to use a minimum number of arguments and local variables because what's left must go on our explicit stack. Here we go. We do away with all except i
and prefix
.
class CombinationLister {
private final int[] counts;
private final int length;
CombinationLister(int[] counts) {
this.counts = counts.clone();
this.length = Arrays.stream(counts).sum();
}
private void list(String prefix) {
if (prefix.length() == length) {
System.out.println(prefix);
}
for (int i = 0; i < counts.length; i++) {
if (counts[i] > 0) {
--counts[i];
list(prefix + (char) ('a' + i));
++counts[i];
}
}
}
void run() {
list("");
}
}
Now let's transcribe to C, which has goto
. Here it's easy to eliminate even prefix
by adding a global string buffer.
#include <stdio.h>
char str[100];
int counts[] = { 1, 2, 3 };
int n_counts = 3;
int total_count = 6;
int len = 0;
void list(void) {
if (len == total_count) printf("%.*s\n", total_count, str);
for (int i = 0; i < n_counts; i++) {
if (counts[i] > 0) {
str[len] = 'a' + i;
--counts[i];
++len;
list();
--len;
++counts[i];
}
}
}
Now, the rules are:
i
left, so we don't even need records. A stack of int
s will do. push
onto the stack, thenrtn:
. rtn:
. These rules essentially mimic the code the compiler will generate to handle recursive calls.
Putting it all together, we have:
int stk[100];
int p = 0; // stack pointer
void list(void) {
int i;
start:
if (len == total_count) printf("%.*s\n", total_count, str);
for (i = 0; i < n_counts; i++) {
if (counts[i] > 0) {
str[len] = 'a' + i;
--counts[i];
++len;
stk[p++] = i; // push i on stack
goto start;
rtn:
--len;
++counts[i];
}
}
// epilog
if (p > 0) {
i = stk[--p]; // restore i from stack
goto rtn;
}
}
If you follow the steps carefully, your code will run first try every time. The only additional tip is that when there is more than one recursive call site, you'll need one return label for each rtn1:
, rtn2:
, etc. and an extra int
field in the stack that connotes the return site, with a switch statement in the epilog to jump to the correct one.
Of course this isn't pretty code. We'd like to get rid of the goto
s. It turns out this is always possible by doing "algebra" to convert the gotos
to loops. There are a couple of dozen transformation techniques...too many to describe here. It's a lot like simplifying an equation in math class. Sometimes it's necessary to add Boolean flags. In this case, though, we don't need to. I finished with this:
void list(void) {
for (int i = 0;;) {
while (i < n_counts && counts[i] == 0) i++;
if (i < n_counts) {
--counts[i];
str[len] = 'a' + i;
stk[p++] = i;
if (++len == total_count) printf("%.*s\n", total_count, str);
i = 0;
} else if (p > 0) {
i = stk[--p];
--len;
++counts[i++];
}
else break;
}
}
Just for fun, back to Java:
class CombinationLister {
private final int[] counts;
private final char[] str;
private final int[] stk;
private int p = 0;
private int len = 0;
CombinationLister(int[] counts) {
this.counts = counts.clone();
this.str = new char[Arrays.stream(counts).sum()];
this.stk = new int[str.length];
}
void run() {
for (int i = 0;;) {
while (i < counts.length && counts[i] == 0) i++;
if (i < counts.length) {
--counts[i];
str[len] = (char) ('a' + i);
stk[p++] = i;
if (++len == str.length) System.out.println(str);
i = 0;
} else if (p > 0) {
i = stk[--p];
--len;
++counts[i++];
} else break;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With