我正在用java做一个8件式的益智游戏,并且这个命令命令我做一个DFS来找到解决方案,从随机状态开始。
我有一个Node类,每个Node对象都知道它在使用int [] []时的状态以及它的父节点是什么。它也知道它可以向哪个方向移动(左,右,上,下)
goal state: start state (random):
[0 1 2] [1 8 0]
[3 4 5] [3 7 4]
[6 7 8] [2 5 6]
从单个节点开始节点开始,我的程序为所有可能的子节点创建一个节点。它检查空白方块可以移动的可用方向,检查它是否回到已经属于另一个节点的状态。因此,在上面的示例情况中,起始节点将扩展如下:
[1 8 0]
A) [3 7 4]
[2 5 6]
/ \
[1 0 8] [1 8 4]
B) [3 7 4] [3 7 0] C)
[2 5 6] [2 5 6]
/ / / \
... ... [1 8 4] [1 8 4]
D) [3 0 7] [3 7 6] E)
[2 5 6] [2 5 0]
/ / / / \
... ... ... [1 8 4] NO
F) [3 7 6] CHILD
[2 0 5]
/ \
... ...
我处理这个问题的方法是我探索第一个节点并将其所有子节点推送到堆栈,这种情况发生在一个递归方法中,该方法循环扩展每个节点,直到达到目标状态或达到截止值(一个数字)我提供以避免永远循环)
stack = [A]
pop()
stack = []
A -> B
push(B)
stack = [B]
A -> C
push(C)
stack = [C|B]
A -> NO MORE CHILDREN
pop()
stack = [B]
C -> D
push(D)
stack = [D|B]
C -> E
push(E)
stack = [E|D|B|]
C -> NO MORE CHILDREN
pop()
stack = [D|B|]
E -> F
push(F)
stack = [F|D|B]
E -> NO MORE CHILDREN
pup()
stack = [D|B]
只要我的截止值不是太高,代码就可以正常工作,如果它比我得到的错误:java.lang.StackOverflowError
我做错了什么?
public void expand(Node parent, int cutoff){
cutoff--;
int[][] currentState = parent.getState();
if(Arrays.deepEquals(currentState, goalState)){
found = true;
goalNode = parent;
}
if(cutoff>0 && !found){
if(parent.up){
Node child = createUPnode();
child.setParent(parent);
stack.push(child);
parent.up = false;
expand(parent, cutoff)
}
else if(parent.right){
Node child = createRIGHTnode();
child.setParent(parent);
stack.push(child);
parent.right = false;
expand(parent, cutoff)
}
else if(parent.down){
Node child = createDOWNnode();
child.setParent(parent);
stack.push(child);
parent.down = false;
expand(parent, cutoff)
}
else if(parent.left){
Node child = createLEFTnode();
child.setParent(parent);
stack.push(child);
parent.left = false;
expand(parent, cutoff)
}
else{
Node nextNode = stack.pop();
expand(nextNode, cutoff);
}
}
else{
System.out.println("CUTOFF REACHED");
}
}
答案 0 :(得分:1)
帖子'What is a stack overflow error?'包含很多关于什么可能导致Java中的StackOverflowError的信息。导致此类错误的最常见原因是错误的递归调用(导致无限循环)。通过为expand()
的递归调用引入一个截止值,您似乎已经防范了这一点。
但即使使用此分隔符,堆栈大小也可能很小,无法处理递归调用。我相信你有两个选择:
1)为你的截止值使用一个“足够小”的值(这实际上是有效的,正如你已经写过的那样),但这限制了你的搜索深度。
2)增加JVM的堆栈大小。您可以通过将标志-Xss1024k
添加为VM参数来执行此操作。有关如何增加堆栈大小的更多信息,请阅读Java stack overflow error - how to increase the stack size in Eclipse?
答案 1 :(得分:0)
使用Dequeue
实现DFS或BFS很简单,没有任何递归,只需循环,从出列的头部读取并生成尾部的新解决方案(BFS)。要使用DSF,请将孩子添加到头部。
我认为您应该使用广度 - 首次搜索来找到最短可能的解决方案,对吧?
如果你确定关于深度优先搜索(我觉得这没什么意义)你可以取消递归并只使用你创建的dequeue
(不是调用堆栈)。不需要重复呼叫。只需循环:通过弹出一个节点来启动每个循环,生成它的子节点并将它们推到出队的头部,然后重新开始。如果dequeue
为空,则没有解决方案......
在大多数情况下,最好先检查生成的解决方案是否已经生成,然后再将其添加到队列末尾,以减少内存使用量。使用普通集合,HashSet可能比TreeSet更快 - 请记住正确实现Node.equals()
和Node.hashCode()
。
我认为普通LinkedList
在这种情况下效率最高Dequeue
- 但为什么不测试自己。