Java DFS堆栈溢出错误
我正在用java做一个8块的益智游戏,赋值命令是我做一个DFS来找到解决方案,从一个随机状态开始 我有一个节点类,每个节点对象都知道它在使用int[]]时处于什么状态,以及它的父节点是什么。它还知道它可以移动的方向(左、右、上、下) 我的程序从一个节点开始,即开始节点,为所有可能的子节点创建一个节点。它检查空白方块可以移动的可用方向,检查它是否返回到已经属于另一个节点的状态。因此,在上面的示例中,开始节点将展开如下:Java DFS堆栈溢出错误,java,tree,stack-overflow,depth-first-search,Java,Tree,Stack Overflow,Depth First Search,我正在用java做一个8块的益智游戏,赋值命令是我做一个DFS来找到解决方案,从一个随机状态开始 我有一个节点类,每个节点对象都知道它在使用int[]]时处于什么状态,以及它的父节点是什么。它还知道它可以移动的方向(左、右、上、下) 我的程序从一个节点开始,即开始节点,为所有可能的子节点创建一个节点。它检查空白方块可以移动的可用方向,检查它是否返回到已经属于另一个节点的状态。因此,在上面的示例中,开始节点将展开如下: [1 8 0] A) [3 7 4]
[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]
/ \
... ...
我处理这个问题的方法是探索第一个节点并将它的所有子节点推到一个堆栈中,这种情况发生在递归方法中,该方法循环扩展每个节点,直到达到目标状态或达到截止点(我提供了一个数字以避免永远循环)
只要我的截止值不太高,代码就可以正常工作,如果它太高,我就会得到错误:java.lang.StackOverflower
我做错了什么
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");
}
}
这篇文章包含了大量关于Java中StackOverflower错误的原因的信息。这种错误最常见的原因是错误的递归调用(导致无限循环)。通过为递归调用expand()
引入截止值,您似乎已经对此进行了防范
但即使使用此分隔符,堆栈大小也可能太小,无法处理递归调用。我相信你有两个选择:
1) 使用一个“足够小”的值作为你的截止值(这实际上是可行的,正如你已经写过的),但这限制了你的搜索深度
2) 增加JVM的堆栈大小。您可以通过添加标志
-Xss1024k
作为VM参数来实现这一点。有关如何增加堆栈大小的更多信息,请阅读使用出列
实现DFS或BFS非常简单,无需任何递归,只需循环,从出列的头部读取,并生成新的尾部解决方案(BFS)。要使用DSF,请将子项添加到头部
我认为你应该先使用宽度-搜索来找到最短的可能的解决方案,对吗
如果您确定深度优先搜索(我认为这没有什么意义),您可以取消递归,只使用您创建的dequeue
(而不是调用堆栈)。不需要重复调用。Just loop:通过弹出一个节点开始每个循环,生成其子节点并将其推到出列的头部,然后重新开始。如果出列
为空,则没有解决方案
在大多数情况下,最好在将生成的解决方案添加到队列末尾之前检查它们是否已经生成,以减少内存使用。使用普通集合时,HashSet可能比TreeSet快-请记住正确实现Node.equals()
和Node.hashCode()
我想在这种情况下,普通的
LinkedList
将是最有效的Dequeue
,但为什么不自己测试一下呢。谢谢,我增加了堆栈大小,消除了堆栈溢出错误,但我觉得解决这个难题需要很长时间,我在网上看到的其他8个拼图至少可以在10秒内解决,我的算法有问题吗?或者DFS花费这么长时间是很自然的吗?也许您在网上看到的解决方案使用了一些启发式步骤。例如,我不认为在执行下拉操作时,当到达当前节点时,必须考虑扩展与UP动作对应的节点。因为你会回到父节点,对吗?这是一个简单的技巧,已经消除了寻找解决方案所需的25%(粗略的,可能是错误的估计)的扩展。我相信你可以想出其他这样的规则来进一步改进搜索!有第三种选择可以消除这个问题。使堆栈显式并消除递归。使用堆栈而不是递归调用。作为示例,请阅读Wikipedia()中描述的迭代树递归示例。我认为消除堆栈是最好的选择。这意味着你可以处理任意复杂的图形,只需要可用的内存,而不是堆栈的大小。是的,我已经有了这个规则,但有时要花一个小时才能完成整个过程,所以我知道我一定是做错了什么。数学不是我最强的科目,但我认为最大可能的状态数是9!这大约是370000,但有时会超出这个范围,所以我认为在检查该州是否已经被访问时有一个错误。我想我只需要把它放在一边一点,然后再回来休息一下。。。再次感谢所有的帮助。。。一旦我弄明白了,我会更新的。OP在帖子中说:“分配命令我做一个DFS来找到解决方案”。显然,我不能停止浪费时间。示例中的顺序为“A、B、C、D”等,即BFS。
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]
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");
}
}