在java中使用循环将递归方法转换为非递归方法
因此,我目前正在制作一个游戏,其中的指令是使用存储在标记索引(在本例中为圆)中的整数在数组中向左或向右移动,直到我们可以将圆移动到数组的最后一个索引为止。数组的最后一个整数始终为0 比如说, [4] 12310,这里我们从圆0(索引)开始 我们向右移动4,4123[1]0 然后向右走1次,4 1 2 3 1[0]。比赛到此结束,我们赢了 对于递归方法,我的代码如下所示:在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
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;
}