这个问题基于我在compsci课上的测试。特别是,我正在努力转换这个功能:
public static void foo(int number) {
if (number > 0) {
foo(number / 2);
System.out.print(number % 2);
}
}
我需要将此函数转换为非递归函数,但我正在努力解决它,因为System.out.print(number % 2)
在递归调用之后发生。
答案 0 :(得分:5)
您可以随时模拟堆栈,但在很多情况下,您可以将其转换为完全无堆栈的解决方案。 (我不是100%肯定,但我认为只有primitive recursive functions才能实现无堆栈转换。我看不出像Ackermann function这样的东西可以在没有某种堆栈的情况下计算出来。)< / p>
无论如何,对于实践中的大多数情况(以及课堂中的所有情况),都可以找到一种方法。在这里我们可以使用反击技巧:
public static void foo(int number) {
for ( int divisor = 1; divisor <= number; divisor *= 2) {
System.out.print( (number / divisor) % 2 );
}
}
<强>更新强> 转换这样的简单函数最简单实用的方法是运行它,在每次迭代后写下输出,然后完全忘记递归,忘记原始代码,单独查看输出并问自己:这是什么代码做什么?然后尝试编写产生相同输出的迭代代码。这项技术在uni上很有用。但它并不总是在现实生活中起作用。 :)
答案 1 :(得分:4)
关于这个问题的另一个观点,因为你已经有了很多答案。
解决递归问题的全覆盖方法是使用堆栈。但是,如果您确切知道要解决的问题,可以找到替代解决方案。或者可能不完全是另类 - 只是一个更紧凑的。
在这个例子中,对于大于零的整数,该函数为您提供参数的二进制表示。
您可能想要使用:
public static void foo(int number) {
if ( number > 0 ) {
System.out.print( Integer.toBinaryString(number) );
}
}
但是,当然,这可能只会让你在测试中获得大胆的分数。
所以这里是对Java实际执行此操作的改编:
public static void foo(int number) {
if (number > 0) {
char[] chars = new char[32];
int charPos = 32;
char[] digits = { '0', '1' };
do {
chars[--charPos] = digits[number % 2];
number /= 2;
} while (number > 0);
System.out.print(new String(chars, charPos, 32 - charPos));
}
}
事实上,它是使用堆栈,但不是非常复杂的堆栈。堆栈只是一个字符数组,你从头开始填充,然后开始。您可以使用此类数组而不是Collection,因为已知事实int
包含的位数不超过32位。所以你永远不会用完数组的边界。说实话,因为你只使用正数,它甚至可以是一个31个字符的数组。
因此,在每个回合中,您将当前数字放在数组中的当前空位置,并将索引向左移动。最后,使用String
的构造函数将收集的所有字符转换为字符串,方便地,可以使用字符数组的指定部分。
Java使用的实际运算符有点不同:
do {
chars[charPos--] = digits[number & 1];
number >>>= 1;
} while (number != 0);
因为移位和掩蔽是比divison更有效的操作。如果您使用此版本,则与使用Integer.toBinaryString()
一样高效。
答案 2 :(得分:3)
您可以使用Deque来跟踪需要打印的内容
public static void foo(int number) {
Deque<Integer> stack = new ArrayDeque<Integer>();
while (number > 0) {
stack.push(number);
number = number/2;
}
//iterate over the stack...
while(!stack.isEmpty()){
Integer myInt = stack.pop();
//your code here
}
}
答案 3 :(得分:3)
也许是prepending
到一个字符串?
public static void foo(int number) {
String r = "";
while(number > 0) {
r = (number%2)+r;
number = number/2;
}
System.out.print(r);
}
答案 4 :(得分:2)
使用堆栈模拟递归的最佳方法,并使用push和pop,因为这就是递归的工作原理:
public static void foo2(int number)
{
Stack st = new Stack();
for(int i=number;i>0;i=i/2)
{
st.push(i%2);
}
while(!st.empty())
{
System.out.print(st.pop());
}
}
答案 5 :(得分:1)
recursion-elimination的通用方法是使用Stack
复制编译器performs recursion的方式。此方法可用于将任何递归程序转换为非递归程序。
我们使用Stack
的方式是存储与每个递归调用相对应的不同stack frames。每个堆栈帧都需要以某种方式在代码执行期间跟踪其位置。我们区分这些堆栈帧(即主要执行步骤)越好,我们的解决方案就越简单。
对于递归函数,每个堆栈帧可以分为两个主要执行部分:
A)检查是否number is > 0
并致电foo
作为参数传递number / 2
B)打印number % 2
或在代码中:
// all marked A are a single "step"
public static void foo(int number) {
if (number > 0) { // A
foo(number / 2); // A
System.out.print(number % 2); // B
}
}
因此,让我们创建一个StackFrame
类来复制它:
static class StackFrame {
int number;
char nep; // Next Execution Position ('A' or 'B')
StackFrame(int number, char nep) {
this.number = number;
this.nep = nep;
}
}
nep
变量用于存储每个StackFrame
的程序执行中的下一个位置。
该计划将处理执行部分A
和B
:
A)程序使用Stack
StackFrames
并在每次复制递归调用时按StackFrame
新number > 0
条件StackFrames
} 是真的。 (这将是递归中的基本情况)
B)一旦达到这个条件(基本情况),我们就可以像编译器一样开始弹出堆栈的number % 2
并打印我们想要的值(public static void iterative(int number) {
Stack<StackFrame> stack = new Stack<StackFrame>();
stack.push(new StackFrame(number, 'A'));
while(!stack.isEmpty()) { // run until we have stack frames
StackFrame top = stack.peek();
switch(top.nep) { // determine next execution step
case 'A':
top.nep = 'B'; // set next execution step
if(top.number / 2 > 0) { // check base case and push current stack frame if base case is true
stack.push(new StackFrame(top.number / 2, 'A'));
}
break;
case 'B':
System.out.print(top.number % 2);
stack.pop(); // end current stack frame
break;
}
}
}
)。
以下是这种方法的外观:
number = 211
Recursive output: 11010011
Iterative output: 11010011
的示例程序输出:
{{1}}
以下是一些用于递归消除的great visual examples