Java StackOverflow错误:如何避免它或将此DFS转换为迭代DFS?
我使用深度优先搜索生成迷宫 M*N个顶点的邻接矩阵是使用DFS以随机顺序遍历的,我只对生成随机路由感兴趣 在顶点数量减少的情况下,这件事可以正常工作,但在使用它时,我遇到了StackOverflow异常Java StackOverflow错误:如何避免它或将此DFS转换为迭代DFS?,java,stack,stack-overflow,depth-first-search,Java,Stack,Stack Overflow,Depth First Search,我使用深度优先搜索生成迷宫 M*N个顶点的邻接矩阵是使用DFS以随机顺序遍历的,我只对生成随机路由感兴趣 在顶点数量减少的情况下,这件事可以正常工作,但在使用它时,我遇到了StackOverflow异常 Graph theGraph = new Graph(1000,1000); 问题: a) 如何使用堆栈将此递归调用更改为迭代调用 b) 有没有办法为方法调用堆栈分配更多内存 class IJ { int i; int j; IJ (int
Graph theGraph = new Graph(1000,1000);
问题:
a) 如何使用堆栈将此递归调用更改为迭代调用
b) 有没有办法为方法调用堆栈分配更多内存
class IJ {
int i;
int j;
IJ (int i,int j){
i = this.i;
j= this.j;
}
}
class Graph {
int M;
int N;
int adjacencyMatrix[][];
ArrayList <IJ> orderOfVisits;
Graph(int M,int N){
this.M=M;
this.N=N;
adjacencyMatrix=new int[M][N];
for (int i=0; i<M; i++)
for (int j=0;j<N;j++){
adjacencyMatrix[i][j]=-1; //mark all vertices as not visited
}
orderOfVisits = new ArrayList<IJ>();
}
void DFS(int i, int j){ // i,j identifies the vertex
boolean northValid= false;
boolean southValid= false;
boolean eastValid = false;
boolean westValid = false;
int iNorth, jNorth;
int iSouth, jSouth;
int iEast, jEast;
int iWest, jWest;
iNorth=i-1;
if (!(iNorth<0)) northValid=true;
iSouth=i+1;
if(!((iSouth)>=M)) southValid=true;
jEast=j+1;
if(!((jEast)>=N)) eastValid=true;
jWest= j-1;
if (!(jWest<0)) westValid=true;
if (adjacencyMatrix[i][j]==-1){ //if the vertex is unvisited
adjacencyMatrix[i][j]=0; //mark the vertex as visited
IJ ij = new IJ(i,j);
orderOfVisits.add(ij); //add the vertex to the visit list
System.out.println("Visit i,j: " + i +" " +j);
Double lottery = Math.random();
for (int rows=i; rows<M; rows++)
for (int cols=j; cols<N; cols++){
if (lottery>0.75D){
if(northValid)
{
DFS(iNorth,j);
}
if(southValid){
DFS(iSouth,j);
}
if(eastValid){
DFS(i, jEast);
}
if(westValid){
DFS(i,jWest);
}
}
else if (lottery<0.25D)
{
if(westValid){
DFS(i,jWest);
}
if(eastValid){
DFS(i, jEast);
}
if(southValid){
DFS(iSouth,j);
}
if(northValid)
{
DFS(iNorth,j);
}
}
else if ((lottery>=0.25D)&&(lottery<0.5D))
{
if(southValid){
DFS(iSouth,j);
}
if(eastValid){
DFS(i, jEast);
}
if(westValid){
DFS(i,jWest);
}
if(northValid){
DFS(iNorth,j);
}
}
else if ((lottery>=0.5D)&&(lottery<=0.75D))
{
if(eastValid){
DFS(i, jEast);
}
if(westValid){
DFS(i,jWest);
}
if(southValid){
DFS(iSouth,j);
}
if(northValid){
DFS(iNorth,j);
}
}
}
} //end nested for
} //end DFS
//
}
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
Graph theGraph = new Graph(1000,1000);
theGraph.DFS(0,0);
}
}
IJ类{
int i;
int j;
IJ(inti,intj){
i=这个。i;
j=这个。j;
}
}
类图{
int M;
int N;
int邻接矩阵[];
ArrayList访问顺序;
图(int M,int N){
这个,M=M;
这个,N=N;
邻接矩阵=新整数[M][N];
对于(int i=0;i关于(b),至少对于Sun/Oracle JVM,您可以使用JVM的-Xss
命令行选项增加堆栈大小。一些伪代码:
Stack<IJ> nodesToVisit;
nodesToVisit.Push(new IJ(0, 1));
nodesToVisit.Push(new IJ(1, 0));
while (nodesToVisit.Count > 0)
{
var ij = nodesToVisit.Pop();
if (visited ij)
continue;
.... mark ij visited
... check north/south/east/west validity
List<IJ> directions = new List<IJ>();
if (canGoNorth)
directions.Add(new IJ(iNorth, j));
if (canGoSouth)
directions.Add(new IJ(iSouth, j));
if (canGoEast)
directions.Add(new IJ(i, jEast));
if (canGoWest)
directions.Add(new IJ(i, jWest));
... randomize list
foreach (direction in directions)
nodesToVisit.Push(direction);
}
堆栈节点访问;
nodesToVisit.Push(新IJ(0,1));
nodesToVisit.Push(新IJ(1,0));
同时(nodesToVisit.Count>0)
{
var ij=nodesToVisit.Pop();
如果(ij)
继续;
……马克·伊杰来访
…检查北/南/东/西有效性
列表方向=新列表();
伊夫(坎格诺特)
增加(新的IJ(iNorth,j));
if(canGoSouth)
增加(新IJ(iSouth,j));
if(canGoEast)
增加(新IJ(i,jEast));
如果(canGoWest)
增加(新的IJ(i,jWest));
…随机化列表
foreach(方向中的方向)
nodesToVisit.Push(方向);
}
基本上:
- 按随机顺序推动堆栈上所有可能的方向
- 挑选最重要的项目
- 去那里
- 重复此操作,直到堆栈为空(不需要访问更多节点)
我不认为增加堆栈限制是解决问题的好办法。您必须将递归实现转换为迭代实现。通常(我在这里也认为),递归算法比执行相同操作的迭代算法更容易理解
原则上,您需要用包含必要信息的显式数据结构(堆栈等)替换Java方法调用堆栈
在您的情况下,它将是当前节点,以及要访问的剩余邻居节点的列表,按访问顺序排列
class DFSNode {
DFSNode parent;
int x, y;
Queue<Direction> neighborsToVisit;
DFSNode(DFSNode p, int x, int y) {
this.parent = p; this.x = x; this.y = y;
this.neighborsToVisit = new ArrayDeque(3);
}
}
enum Direction {
// TODO: check the numbers
NORTH(0,1), SOUTH(0,-1), EAST(1,0), WEST(-1,0);
Direction(int dX, dY) {
deltaX = dX; deltaY = dY;
}
private int deltaX, deltaY;
int nextX(int x) { return x + deltaX; }
int nextY(int y) { return y + deltaY; }
}
void visitNode(DFSNode node) {
// TODO: check which adjacent directions are valid,
// randomize the order of these adjacent directions,
// fill them in the queue.
}
void visitGraph(int x, int y) {
DFSNode currentNode = new DFSNode(null,x,y);
visitNode(currentNode);
while(currentNode != null) {
Direction dir = currentNode.neighboursToVisit.poll();
if(dir == null) {
// all neighbours of this node already visited
// ==> trackback to parent (and end if this is root node)
currentNode = currentNode.parent;
continue;
}
currentNode = new DFSNode(currentNode, dir.nextX(currentNode.x), dir.nextY(currentNode.y));
visitNode(currentNode);
}
}
class节点{
节点父节点;
int x,y;
让邻居排队参观;
DFSNode(DFSNode p,int x,int y){
this.parent=p;this.x=x;this.y=y;
this.neightorstovisit=new ArrayDeque(3);
}
}
枚举方向{
//TODO:检查一下数字
北(0,1)、南(0,-1)、东(1,0)、西(1,0);
方向(整数dX,dY){
deltaX=dX;deltaY=dY;
}
德尔泰私人酒店;
int nextX(int x){return x+deltaX;}
intnexty(inty){returny y+deltaY;}
}
无效visitNode(DFSNode节点){
//TODO:检查哪些相邻方向有效,
//随机化这些相邻方向的顺序,
//把他们排在队列里。
}
无效访问图(整数x,整数y){
DFSNode currentNode=新的DFSNode(null,x,y);
visitNode(当前节点);
while(currentNode!=null){
Direction dir=currentNode.neighturstovisit.poll();
if(dir==null){
//此节点的所有邻居都已访问
//==>trackback到父节点(如果这是根节点,则结束)
currentNode=currentNode.parent;
继续;
}
currentNode=新的DFSNode(currentNode,dir.nextX(currentNode.x),dir.nextY(currentNode.y));
visitNode(当前节点);
}
}
visitNode
将包含主逻辑,即DFS方法中现在的内容。它将用四个方向中的某些方向(我认为最多3个)填充队列,顺序由random()
结果决定。我希望您能找到这一帮助
您可以使用-Xss选项增加堆栈大小,也可以重写代码
代码:
public void dfsPostOrderIterative(AdjGraph图、AdjGraph.Node顶点、回调){
Stack toVisit=新堆栈();
toVisit.push(新级别(Collections.singletonList(vertex))
while(!toVisit.isEmpty()){
Level=toVisit.peek();
如果(level.index>=level.nodes.size()){
toVisit.pop();
继续;
}
AdjGraph.Node节点=level.nodes.get(level.index);
如果(!node.isvisted()){
if(node.isChildrenExplored()){
node.markvisted();
callback.nodeVisited(图,节点);
level.index++;
}否则{
列表边=图形边(节点);
List outgoing=Lists.newArrayList(Collections2.filter(edges,new Predicate()){
@凌驾
公共布尔应用(AdjGraph.Node输入){
return!input.isChildrenExplored();
}
}));
if(outgoing.size()>0)
toVisit.add(新级别(传出));
node.markChildrenExplored();
}
}否则{
level.index++;
}
}
}为什么“IJ”构造函数设置i=this.i;j=this.j
?您没有初始化成员i
和j
?
while (!toVisit.isEmpty()) {
Level level = toVisit.peek();
if (level.index >= level.nodes.size()) {
toVisit.pop();
continue;
}
AdjGraph.Node node = level.nodes.get(level.index);
if (!node.isVisited()) {
if (node.isChildrenExplored()) {
node.markVisited();
callback.nodeVisited(graph, node);
level.index++;
} else {
List<AdjGraph.Node> edges = graph.edges(node);
List<AdjGraph.Node> outgoing = Lists.newArrayList(Collections2.filter(edges, new Predicate<AdjGraph.Node>() {
@Override
public boolean apply(AdjGraph.Node input) {
return !input.isChildrenExplored();
}
}));
if (outgoing.size() > 0)
toVisit.add(new Level(outgoing));
node.markChildrenExplored();
}
} else {
level.index++;
}
}