*** 错误:我怎样才能避免它或将这个 DFS 变成一个迭代的?
Posted
技术标签:
【中文标题】*** 错误:我怎样才能避免它或将这个 DFS 变成一个迭代的?【英文标题】:*** error: How can I avoid it or turn this DFS into an iterative one? 【发布时间】:2011-08-15 02:57:24 【问题描述】:我正在使用深度优先搜索来生成迷宫。
使用 DFS 以随机顺序遍历 M*N 个顶点的邻接矩阵,我只对生成随机路线感兴趣。
这个东西在减少顶点数量的情况下工作得很好,但是在使用它时我得到了一个 *** 异常
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'构造函数设置i = this.i; j = this.j
?您没有初始化成员 i
和 j
?
【参考方案1】:
一些伪代码:
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);
基本上:
以随机顺序将所有可能的方向推入堆栈 选择最上面的项目 去那里 重复直到堆栈为空(也没有要访问的节点)我不认为增加堆栈限制是解决问题的好方法。
【讨论】:
【参考方案2】:关于 (b),至少对于 Sun/Oracle JVM,您可以使用 JVM 的-Xss
命令行选项来增加堆栈大小。
【讨论】:
【参考方案3】:您必须将递归实现转换为迭代实现。通常(我也认为在这里)递归算法比做同样事情的迭代算法更容易理解。
原则上,您需要将 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);
visitNode
将包含主要逻辑,即您的 DFS 方法中现在的内容。它不会递归,而是按照random()
结果确定的顺序用四个方向中的一些(我认为最多 3 个)填充队列。
【讨论】:
【参考方案4】:希望对您有所帮助。
您可以使用 -Xss 选项增加堆栈大小或重写代码。你可以在这里得到一些想法。
http://www.vvlasov.com/2013/07/post-order-iterative-dfs-traversal.html
代码:
public void dfsPostOrderIterative(AdjGraph graph, AdjGraph.Node vertex, Callback callback) Stack toVisit = new Stack(); toVisit.push(new Level(Collections.singletonList(vertex)));
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++;
【讨论】:
以上是关于*** 错误:我怎样才能避免它或将这个 DFS 变成一个迭代的?的主要内容,如果未能解决你的问题,请参考以下文章