在java中使用循环将递归方法转换为非递归方法

在java中使用循环将递归方法转换为非递归方法,java,performance,list,loops,recursion,Java,Performance,List,Loops,Recursion,因此,我目前正在制作一个游戏,其中的指令是使用存储在标记索引(在本例中为圆)中的整数在数组中向左或向右移动,直到我们可以将圆移动到数组的最后一个索引为止。数组的最后一个整数始终为0 比如说, [4] 12310,这里我们从圆0(索引)开始 我们向右移动4,4123[1]0 然后向右走1次,4 1 2 3 1[0]。比赛到此结束,我们赢了 对于递归方法,我的代码如下所示: public static boolean rightWing (int circle, int[] game, List&l

因此,我目前正在制作一个游戏,其中的指令是使用存储在标记索引(在本例中为圆)中的整数在数组中向左或向右移动,直到我们可以将圆移动到数组的最后一个索引为止。数组的最后一个整数始终为0

比如说,

[4] 12310,这里我们从圆0(索引)开始

我们向右移动4,4123[1]0

然后向右走1次,4 1 2 3 1[0]。比赛到此结束,我们赢了

对于递归方法,我的代码如下所示:

public static boolean rightWing (int circle, int[] game, List<Integer> checkerList){

int last = game.length-1;

if (circle == last){ // base case for recursion
    return true;
}

if (circle < 0){ // if we go out of bounds on the left
    return false;
}

if (circle > last){ // if we go out of bounds on the right
    return false;
}

if (checkerList.contains(circle)){ // check for the impossible case 
    return false;
}

checkerList.add(circle); // adds the circle value for the last check to checkerList so we can check for the impossible case

int moveRight = circle + game[circle]; // these two integers help the game move according to the value of the int at circle
int moveLeft = circle - game[circle];

return rightWing( moveRight, game, checkerList) || rightWing(moveLeft, game,checkerList);
}
公共静态布尔右翼(int循环,int[]游戏,列表检查列表){
int last=game.length-1;
if(circle==last){//递归的基本情况
返回true;
}
如果(圆<0){//如果我们在左边越界
返回false;
}
如果(圆圈>最后一个){//如果我们在右边越界
返回false;
}
if(checkerList.contains(circle)){//检查不可能的情况
返回false;
}
checkerList.add(circle);//将上次检查的圆值添加到checkerList,以便我们可以检查不可能的情况
int moveRight=circle+game[circle];//这两个整数帮助游戏根据circle处的int值移动
int moveLeft=圆圈-游戏[圆圈];
返回右翼(移动右键,游戏,棋子列表)| |右翼(移动左键,游戏,棋子列表);
}
这很有效,但唯一的问题是它是递归的,速度很慢。我试图使用循环和堆栈/队列来重新设计它,以提高效率,但写了这篇文章(用伪代码)后,我陷入了困境:

Boolean右翼(整数圈、列表游戏、列表checkerList)
Int lastPlace=game.size()-1

对于int i来说,最重要的一点是:在调试应用程序时,您应该首先收集一些性能数据,以确定应用程序在哪里花费了大部分时间。否则,修复性能是低效的。您可以使用
jvisualvm
它与jdk捆绑在一起

数据结构主宰着性能世界 它之所以会慢是因为:

if (checkerList.contains(circle)){ // check for the impossible case 
    return false;
}
列表中的项目越多,速度越慢。对于
contains
方法,列表具有
线性复杂度。如果要使用
HashSet
,可以将其设置为
常量复杂性。例如,如果列表包含100个元素,则使用
list
时,此部分的速度将比使用
HashSet
时慢100倍左右

另一件可能需要花费一些时间的事情是装箱/拆箱:每次将元素放入列表时,
int
被包装到新的
Integer
对象中,这称为装箱。您可能希望使用
IntSet
来避免装箱/取消装箱并节省GC时间

转换为迭代形式 我不认为这会影响您的应用程序速度,只是为了答案的完整性

将递归应用程序转换为迭代形式非常简单:封面下的每个方法参数在每次调用(或其他函数)时都存储在一个隐藏堆栈中。在转换过程中,您只需创建自己的堆栈并手动管理它

public static boolean rightWingRecursive(int circle, int[] game) {
    Set<Integer> checkerList = new HashSet<Integer>();
    Deque<Integer> statesToExplore = new LinkedList<>();
    
    int last = game.length - 1;
    
    statesToExplore.push(circle);
    
    while (!statesToExplore.isEmpty()) {
        int circleState = statesToExplore.pop();
        
        if (circleState == last) { // base case for recursion
            return true;
        }

        if (circleState < 0) { // if we go out of bounds on the left
            continue;
        }
        
        if (circleState > last) { // if we go out of bounds on the right
            continue;
        }
        
        if (checkerList.contains(circle)) { // check for the impossible case
            continue;
        }
        
        checkerList.add(circle); // adds the circle value for the last check to
        // checkerList so we can check for the
        // impossible case
        int moveRight = circle + game[circle]; // these two integers help the
        // game move according to the
        // value of the int at circle
        int moveLeft = circle - game[circle];
        statesToExplore.push(moveRight);
        statesToExplore.push(moveLeft);
    }

    return false;
}
public静态布尔rightWingRecursive(int循环,int[]游戏){
Set checkerList=new HashSet();
Deque statesToExplore=new LinkedList();
int last=game.length-1;
状态探索。推(圈);
而(!statesToExplore.isEmpty()){
int circleState=statesToExplore.pop();
if(circleState==last){//递归的基本情况
返回true;
}
如果(circleState<0){//如果我们在左边越界
继续;
}
如果(circleState>last){//如果我们在右边出界
继续;
}
if(checkerList.contains(circle)){//检查不可能的情况
继续;
}
checkerList.add(圆);//将上次检查的圆值添加到
//checkerList,以便我们可以检查
//不可能的情况
int moveRight=circle+game[circle];//这两个整数有助于
//游戏按照游戏规则移动
//圆的int值
int moveLeft=圆圈-游戏[圆圈];
statesToExplore.push(向右移动);
statesToExplore.push(向左移动);
}
返回false;
}

它有多慢?典型的数组大小是多少?@lesterpierson 123特别感谢您的示例输入,并花时间创建一个最小的可验证示例。您所说的底部不可能的情况是什么意思?我也很难理解您对deque语句的实现探索…@lesterpierson它是如何工作的:我们从最初的位置(状态)和每个动作,我们可以移动到另一个步骤。我们一次只能探索一个步骤,因此每当我们有多个备选方案时,我们记录它们(通过将可能的状态推送到statesToExplore队列),然后检查其中一个。考试通常会给我们更多的检查步骤。最后,我们将到达一个地方,在那里,没有进一步的步骤会导致添加新的有效步骤(例如,所有步骤都将超出边界)。此外,最后一次返回也不是不可能的。这意味着我们已经尝试了所有可能的状态,并且从未达到目标条件
return true
,这意味着我们应该返回
false
public static boolean rightWingRecursive(int circle, int[] game) {
    Set<Integer> checkerList = new HashSet<Integer>();
    Deque<Integer> statesToExplore = new LinkedList<>();
    
    int last = game.length - 1;
    
    statesToExplore.push(circle);
    
    while (!statesToExplore.isEmpty()) {
        int circleState = statesToExplore.pop();
        
        if (circleState == last) { // base case for recursion
            return true;
        }

        if (circleState < 0) { // if we go out of bounds on the left
            continue;
        }
        
        if (circleState > last) { // if we go out of bounds on the right
            continue;
        }
        
        if (checkerList.contains(circle)) { // check for the impossible case
            continue;
        }
        
        checkerList.add(circle); // adds the circle value for the last check to
        // checkerList so we can check for the
        // impossible case
        int moveRight = circle + game[circle]; // these two integers help the
        // game move according to the
        // value of the int at circle
        int moveLeft = circle - game[circle];
        statesToExplore.push(moveRight);
        statesToExplore.push(moveLeft);
    }

    return false;
}