Java 如何最好地将递归函数转换为迭代函数?

Java 如何最好地将递归函数转换为迭代函数?,java,recursion,Java,Recursion,这个问题是基于我在compsci课堂上的一次测试。特别是,我正在努力转换此函数: public static void foo(int number) { if (number > 0) { foo(number / 2); System.out.print(number % 2); } } 我需要将此函数转换为非递归函数,但我正在努力使用它,因为System.out.print(数字%2)发生在递归调用之后。当然,您可以始终模拟堆栈,但

这个问题是基于我在compsci课堂上的一次测试。特别是,我正在努力转换此函数:

public static void foo(int number)  {
    if (number > 0) {
        foo(number / 2);
        System.out.print(number % 2);
    }
}

我需要将此函数转换为非递归函数,但我正在努力使用它,因为
System.out.print(数字%2)
发生在递归调用之后。

当然,您可以始终模拟堆栈,但在许多情况下,您可以将其转换为完全无堆栈的解决方案。(我不是100%确定,但我认为无堆栈转换只适用于。如果没有某种堆栈,我看不出有任何方法可以计算类似的内容。)

无论如何,对于实践中的大多数案例(以及教室中的所有案例),都有可能找到一种方法。在这里,我们可以使用反技巧:

public static void foo(int number)  {
    for ( int divisor = 1; divisor <= number; divisor *= 2) {
      System.out.print( (number / divisor) % 2 );
    }
}
publicstaticvoidfoo(整数){
对于(int divisor=1;divisor,您可以使用a来跟踪需要打印的内容

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
    }
}
publicstaticvoidfoo(整数){
Deque stack=new ArrayDeque();
而(数量>0){
堆栈推送(数字);
数量=数量/2;
}
//在堆栈上迭代。。。
而(!stack.isEmpty()){
整数myInt=stack.pop();
//你的代码在这里
}
}

可能是
在字符串前面加上

public static void foo(int number) {
    String r = "";
    while(number > 0) {
        r = (number%2)+r;
        number = number/2;  
    }
    System.out.print(r);
}

模拟递归的最佳方法是使用堆栈,并使用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());
   }
}
一种通用方法是通过使用
堆栈来复制编译器的工作方式。这种方法可用于将任何递归程序转换为非递归程序

我们使用
堆栈的方法是存储每个递归调用对应的不同堆栈帧。每个堆栈帧需要在代码执行期间以某种方式跟踪其位置。我们越能区分这些堆栈帧(即主要执行步骤),解决方案就越简单

对于递归函数,每个堆栈帧可以分为两个主要执行部分:


A)检查
number是否为>0
并调用
foo
传递
number/2
作为参数

B)打印
编号%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)程序使用
StackFrames
堆栈
,并在每次复制递归调用时推送一个新的
StackFrames
,而条件
number>0
为真。(这将是递归中的基本情况

B)一旦达到此条件(基本情况),我们就可以开始弹出堆栈的
StackFrames
,方法与编译器一样,并打印所需的值(
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;
        }
    }
}

这里有一些递归消除的方法,只是从另一个角度来看这个问题,因为你已经有了很多答案

确实,解决递归的覆盖所有方法是使用堆栈。但是,如果您确切知道要解决的问题,您可以找到替代解决方案。或者可能不完全是替代解决方案-只是一个更紧凑的解决方案

在本例中,该函数为大于零的整数提供参数的二进制表示形式

您可能会尝试使用:

   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));
    }
}
事实上,它使用的是一个堆栈,但不是一个非常复杂的堆栈。堆栈只是一个字符数组,从末尾开始填充,然后开始填充。你可以使用这样的数组而不是集合,因为众所周知,
int
包含的字符不超过32位。因此你永远不会超出t的界限说实话,因为你只处理正数,它甚至可以是一个31个字符的数组

因此,在每次循环中,您都将当前数字放在数组中的当前空位置,并将索引向左移动。最后,您使用
string
构造函数将收集的所有字符转换为字符串,该构造函数可以方便地使用字符数组的指定部分

Java使用的实际运算符有点不同:

        do {
            chars[charPos--] = digits[number & 1];
            number >>>= 1;
        } while (number != 0);

因为移位和掩蔽比除法更有效。如果使用此版本,它的效率与使用
Integer.tobinarysting()

一样。递归方法将其状态存储在调用堆栈上。通常,迭代解决方案将以“相反”的方式进行操作顺序,因此如果您需要准确地转换行为,请使用您自己的堆栈在循环中存储状态,并在完成时将其弹出。预期的输出是什么?需要注意的最重要的事情是递归例程是否在调用堆栈上存储状态。如果它存储状态,则需要找出如何在迭代过程中再现该状态环境。这通常意味着您必须维护类似堆栈的数据结构来存储状态。如果状态未存储在调用堆栈上(与调用/返回本身相关的明显状态除外)然后用迭代方案替换是很简单的。这种递归让我很困惑。如果有人能告诉我预期的输出,我可以将它写在一个循环中。我不在我的电脑上。我会运行并检查它。:)只是给你提示:输出是输入的二进制表示。@nem谢谢,但它并没有真正的创造性。没有更多的创造性比起解决数独难题,我觉得两者都使用了一套相当小的可以练习的技巧。只是我们不能马上用这种方式解决问题,所以这让我很兴奋
        do {
            chars[charPos--] = digits[number & 1];
            number >>>= 1;
        } while (number != 0);