Java中的高级递归

时间:2010-11-13 01:45:08

标签: java algorithm recursion

我无法理解递归,尤其是复杂的例子。如果有人花些时间解释一下,我真的很感激。我确实有4张纸都跟着我追踪这个功能,但我不知道怎么把它放在一起。

public static String shortestPath(int x, int y, int tX, int tY,boolean blocked[][]) {

        if(x>blocked.length-1 || y>blocked[0].length-1 || x<0 || y<0 )
            return null;
        if(blocked[x][y]==true)
            return null;
        if(x==tX && y==tY)
            return "";

        String paths[]=new String[4];
        blocked[x][y]=true; //this just means this coordinate is blocked, so dont use it
        paths[0]=shortestPath(x, y+1, tX, tY, blocked);
        paths[1]=shortestPath(x, y-1, tX, tY, blocked);
        paths[2]=shortestPath(x+1, y, tX, tY, blocked);
        paths[3]=shortestPath(x-1, y, tX, tY, blocked);
        blocked[x][y] = false;
        int result=findShortestString(paths, 0, 3); 
//findShortestString just takes an array of strings, 
//with 0 being the lo index and 3 being the hi, 
//and returns the index that contains the string with the shortest length.
        //5
        if(paths[result]==null)
           return null;
        else{

           if(result==0)
                return 'N' + paths[result];
           if(result==1)
                return 'S' + paths[result];
           if(result==2)
                return 'E' + paths[result];
           if(result==3)
                return 'W' + paths[result];}

        return paths[result];

所以这段代码的作用是,给定一个x和y参数,它会告诉你为了达到tX和tY参数你必须做的最短移动组合(NSWE,北,西,东) 。代码完美无缺,但我不知道如何。

当我尝试跟踪哪些路径[0]计算时,它总是变为空,因为y将始终保持递增,直到它超出边界,其中它返回null。这与路径[1] [2]和[3]的情况相同,它们都返回null,不是吗?那么这个功能是如何工作的呢?

3 个答案:

答案 0 :(得分:6)

首先尝试使用一个简单的小例子 - 1x1网格,没有阻塞的单元格。正如预期的那样,没有任何动作。 x == tXy == tY所以你返回空字符串。好到目前为止。

现在看一下2x2网格,你在NW角落,目的地是NE。

| @ | ^ |
| o | o |

当您尝试向东移动并设置paths[0]时,它会调用shortestPath,阻止您当前的单元格,并将新位置设置为您下方的位置。现在你有了

| X | ^ |
| @ | o |

在那个调用中,它会尝试去N,W,S和E.为了简单起见,忽略东向西发生,所以我们可以立即完成其他3个方向 - 它们都会再次调用{{1在一个无效的位置(2个超出界限,1个你去过)并立即返回shortestPath。你将离开东部,新的网格和位置如下:

null

同样,3个方向返回| X | ^ | | X | @ | 。只有北方会给你你想要的最终结果。当你试图去那里时,你再次调用null,它立即返回空字符串,因为该板现在看起来像这样:

shortestPath

现在我们来结束调用堆栈:

  1. 因为| X | @^ | | X | X | 是空字符串而其他3是paths[1]null是1(我假设你的字符串函数是如何工作的)。因此,您将result返回上一个电话。
  2. 之前的通话将显示"N"paths[0] == nullpaths[1] == null,但关键paths[3] == nullpaths[2]。因此"N"将为2,您将返回result之前的电话。
  3. 从现在开始,您将返回"EN"的第一次调用,这将包含我们做出的第一个选择 - 从起始位置向南移动。但我们还有一个选择 - 去东部。所以你关注那棵树,只是shortestPath

    然后是最后一步,您可以看到哪个字符串较小,并且""当然小于""。因此"EN"为2,因此您在字符串前加result前缀,"E"是您的最终答案。

    现在用它来弄清楚更大的例子。它有助于绘制决策树和每个节点的电路板状态。当你到达树叶时,绘制箭头返回到表示返回值的父节点。这将有很大帮助。

答案 1 :(得分:1)

试着猜猜你在想什么 -

您可能正在描绘4条执行路径:

路径0:shortestPath(x,y + 1,tX,tY,阻塞) - &gt; shortestPath(x,y + 1,tX,tY,阻止) - &gt; ...

路径1:shortestPath(x,y-1,tX,tY,阻塞) - &gt; shortestPath(x,y-1,tX,tY,阻止) - &gt; ...

路径2:shortestPath(x + 1,y,tX,tY,阻塞) - &gt; shortestPath(x + 1,y,tX,tY,阻止) - &gt; ...

路径3:shortestPath(x-1,y,tX,tY,阻塞) - &gt; shortestPath(x-1,y,tX,tY,阻止) - &gt; ...

实际上,执行路径构成了一棵树。每次对shortestPath的调用都会产生4次调用shortestPath,“path0”调用,“path1”调用,“path2”调用和“path3”调用。

因此,您将获得一个执行路径,即path0,path0,path0 ......将返回null。

但是,大多数路径都是不同呼叫的组合。

当递归返回第一个shortestPath调用时,paths [0]将包含FIRST移动为shortestPath(x,y + 1,tX,tY,阻塞)的最短路径,path [1]为FIRST的最短路径move是shortestPath(x,y-1,tX,tY,阻塞)等等。

答案 2 :(得分:1)

这并不复杂。

此部分检查x或y参数是否有效(在边界或未阻止)

if(x>blocked.length-1 || y>blocked[0].length-1 || x<0 || y<0 )
   return null;
if(blocked[x][y]==true)
   return null;

这里检查位置是否到达目的地

if(x==tX && y==tY)
   return "";

现在到递归部分,这个函数会递归到另外四个函数,每个函数都有一个相对于当前位置的可用NSWE方向:

String paths[]=new String[4];
blocked[x][y]=true; //this just means this coordinate is blocked, so dont use it
paths[0]=shortestPath(x, y+1, tX, tY, blocked);
paths[1]=shortestPath(x, y-1, tX, tY, blocked);
paths[2]=shortestPath(x+1, y, tX, tY, blocked);
paths[3]=shortestPath(x-1, y, tX, tY, blocked);
blocked[x][y] = false;
int result=findShortestString(paths, 0, 3);

然后比较递归函数找到的每个路径,找到可用的最短路径/方向串。

如果每个字符串都为null,则findShortestString()可能返回null,因此无法从该递归的起始位置到达目标。

递归的当前位置被标记为已阻止,因此算法不会返回之前访问过的位置。

if(paths[result]==null)
    return null;

检查findShortestString是否找不到任何有效路径。

最后,相对于递归中当前位置的路径被附加到找到最短路径的递归调用的方向。

实施例: 让我们说地图只有一条通往目的地的有效路径,所有其他位置都被淹没。起始位置为[0] [0],目的地为[1] [1](x + 1 = N,y + 1 = E) MAP:

(y-line increases upwards, x-column increases rightwards)
0D
SX

S-start
X-blocked
0-not blocked
D-destination

第一个电话:

-x,y are within boundaries and are not the destination
-blocks current positions([0][0])
-calls function for y = y+1 -> is blocked (returns NULL)
-calls function for y = y-1 -> out of boundaries (returns NULL)
-calls function for x = x+1 -> path is ok

递推:

-blocks current position[1][0]
-calls function for y = y+1 -> Destination!(returns "")
-calls function for y = y-1 -> out of boundaries(returns NULL)
-calls function for x = x+1 -> out of boundaries(returns NULL)
-calls function for x = x-1 -> blocked(returns NULL) (this would be the starting position)
Since paths[0] has the shortest string("") and the result is 'N'+""

(回到第一次迭代)

-calls function for x = x-1 -> out of boundaries(returns NULL)

由于路径[2]的字符串最短,因此结果为“E”+“N”。这是正确的。

由于始终首先调用y = y + 1,因此递归会一直运行直到超出边界。然后它将测试最后位置周围的其他位置,等等。