这个简单的递归代码发生了什么?

时间:2016-12-27 21:05:42

标签: java recursion

我似乎无法理解为什么会打印以下代码

int offScreenLimitLeft = position-10;
int offScreenLimitRight = position+10;
if (offScreenLimitLeft>=0){
    itemsArrayList.get(offScreenLimitLeft).myObj.bitmap = null;
}
if (offScreenLimitRight<=itemsArrayList.size()-1){
    itemsArrayList.get(offScreenLimitRight).myObj.bitmap = null;

}

按照我的看法,它应该打印2个二进制数字的排列。

请不要修复我的代码。我想要解释它为什么会以现在的方式运作。

00
1
10
1

3 个答案:

答案 0 :(得分:4)

如果这与家庭作业相关,则应将其标记为。

您的代码使用0{prev}\n1{prev}等格式生成文本,其中{prev}是递归调用的结果。请注意,此换行符将成为递归结果的一部分,因此会中断&#34;中断&#34;其他电话的结果。让我告诉你我的意思。

n == 0是基本案例,并且硬编码以返回空字符串("")。

n == 1是第一个可以递归的案例。由于{prev}是空字符串,因此返回0\n1。这打印为

0
1
接下来是

n == 2{prev}0\n1,因此生成00\n1\n10\n1。如您所知,这打印为

00
1
10
1

n == 3是我要展示的最后一步。 {prev}00\n1\n10\n1,因此会生成000\n1\n10\n1\n100\n1\n10\n1,其打印为

000
1
10
1
100
1
10
1

作为视觉辅助,我将使用不同的大括号在打印输出中包装不同的递归返回。大括号括起n == 0个结果,方括号括出n == 1个结果,括号括起n == 2个结果。

0(0[0{}
1{}]
1[0{}
1{}])
1(0[0{}
1{}]
1[0{}
1{}])

修改

OP说这不是作业作业,所以我改变了我的答案,提供了一个样本Java程序,它将打印0到2 ^ bits - 1的所有数字。

public class Main {
    public static void main( String[] args ) {
        String binaryNumbers = buildBinaryNumbersString(4);
        System.out.println(binaryNumbers);
    }

    private static String buildBinaryNumbersString( int bits ) {
        return recursivelyBuildBinaryNumbersString(bits, "");
    }

    private static String recursivelyBuildBinaryNumbersString( int bits, String prefix ) {
        String result;
        if (bits <= 0) {
            result = prefix;
        } else {
            result =
                recursivelyBuildBinaryNumbersString(bits - 1, prefix + "0") +
                "\n" +
                recursivelyBuildBinaryNumbersString(bits - 1, prefix + "1");
        }
        return result;
    }
}

(你可能会注意到我改变了我对新行需要处于基本情况的想法。这导致返回值没有可能无关的尾随新的。)

此代码打印

0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

此代码与您的代码非常相似,但区别在于它的工作原理。你的代码可以被认为是试图构建所有二进制字符串&#34;在&#34;一个值,然后尝试扩展这些值以包含下一个更高的位(尽管代码没有成功执行此操作)。相反,编写此代码是为了在多个递归路径中重复使用,前面加上&#34;数字&#34;到01的下一位值。因此,recursivelyBuildBinaryNumbersString()的调用对于相同的bits值会有很大差异,具体取决于传递的prefix。当prefix始终为bits时,请考虑为所有这些1值生成的内容:

prefix: result
000: 0000\n0001
001: 0010\n0011
010: 0100\n0101
011: 0110\n0111
100: 1000\n1001
101: 1010\n1011
110: 1100\n1101
111: 1110\n1111

看看这8个前缀和它们的8个输出如何组合产生4位的全部16个排列?

值得注意的是,这种技术可以通过递归调用将结果传递到目前为止。这种技术与使用递归调用将结果返回给调用链一样有用。值得一提的是,这种技术对于尾递归至关重要。对于支持尾递归(而不是Java)的语言,以及函数的返回值是对同一函数的递归调用,可以编写递归函数,以便该函数执行的最后一个操作是tail call。简而言之,这意味着不再需要调用函数的堆栈条目,因为被调用函数的堆栈条目足以生成调用函数的返回。这允许调用函数的堆栈条目被被调用函数的堆栈条目覆盖,消除了堆栈溢出的危险。我不知道是否每个递归函数都可以用使用尾调用的等效递归函数替换,但即使如此,我发现将状态正在通过调用链向下传递非常有用。

答案 1 :(得分:1)

如果您想了解递归函数的工作原理,最好将其视为树。对于功能:

public static String addZ(int n)
{
    String str ="";
    if(n==0)
        return "";
    str += "0" + addZ(n-1)+"\n";
    str += "1" + addZ(n-1);
    return str;
}

我们可以立即看到两个str +=实际上只是一个追加到字符串的调用:"0" + addZ(n-1) + "\n" + "1" + addZ(n-1)

现在,如果我们向Console添加一些日志记录,我们会得到一个这样的树:

str += "0" + addZ([n=2] n-1)+"n";
    str += "0" + addZ([n=1] n-1)+"n";
        addZ(n = 0) = ""
    str += "1" + addZ([n=1] n-1);
        addZ(n = 0) = ""
str += "1" + addZ([n=2] n-1);
    str += "0" + addZ([n=1] n-1)+"n";
        addZ(n = 0) = ""
    str += "1" + addZ([n=1] n-1);
        addZ(n = 0) = ""

此输出的优点是您可以将其向上滚动

str += "0" + {addZ([n=2] n-1) = "0" + "" + "\n" + "1" + ""} +"\n";
    str += "0" + {addZ([n=1] n-1) = ""} +"\n";
        addZ(n == 0) = ""
    str += "1" + {addZ([n=1] n-1) = ""};
        addZ(n == 0) = ""
str += "1" + {addZ([n=2] n-1) = "0" + "" + "\n" + "1" + ""};
    str += "0" + {addZ([n=1] n-1) = ""}+"\n";
        addZ(n == 0) = ""
    str += "1" + {addZ([n=1] n-1) = ""};
        addZ(n == 0) = ""

因此

addZ(n = 2) = "0" + {"0" + "" + "\n" + "1" + ""} +"\n" "1" + {"0" + "" + "\n" + "1" + ""}
addZ(n = 2) = "00\n1\n10\n1"

您输出的是:

00
1
10
1

如果您希望体验大n的值,我建议您在代码中添加日志记录,例如:https://dotnetfiddle.net/MqqnuM

答案 2 :(得分:1)

使用递归,您将函数调用添加到堆栈顶部,直到达到基本情况,然后开始从堆栈中弹出调用。

addZ(2)的情况下,str被声明为空字符串,然后给出值“0”+ addZ(2-1)或addZ(1)的返回值。

addZ(1)声明它是自己的空str,赋值为“0”+ addZ(1-1)或addZ(0)的返回值。

addZ(0)是基本情况,将空String返回addZ(1)

addZ(1)仍然只有一个值为“0”的str,它会添加换行符“\ n”。

addZ(1)然后将值“1”添加到str + addZ(0)的返回值,我们从上面知道这是一个空字符串。

addZ(1)将str返回addZ(2),值为“0 \ n1”

addZ(2)继续向str添加换行符。

此时str =“00 \ n \ 1 \ n”,这是您的输出样本显示的内容。

addZ(2)然后将“1”+ addZ(2-1)或addZ(1)的返回值添加到str。

从上面,我们知道addZ(1)最终会返回“0 \ n1”

str现在保持值“00 \ n \ 1 \ n10 \ n1”并且函数已完成。

基本上,只需遵循逻辑。如果你很难绕过递归,也可以使用调试器逐步完成它。