Java 如何最好地将递归函数转换为迭代函数?
这个问题是基于我在compsci课堂上的一次测试。特别是,我正在努力转换此函数: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)发生在递归调用之后。当然,您可以始终模拟堆栈,但
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);