Java 用于递归深度优先搜索以存储路径的额外空间
我使用深度优先搜索来识别有向加权图中的路径,同时重新访问属于循环的节点,并根据总行驶距离或从源节点的站点设置截止条件 据我所知,对于递归,深度优先搜索不需要显式堆栈结构,因此我想知道是否可以通过不使用显式堆栈来进一步简化下面的代码:Java 用于递归深度优先搜索以存储路径的额外空间,java,algorithm,recursion,graph,depth-first-search,Java,Algorithm,Recursion,Graph,Depth First Search,我使用深度优先搜索来识别有向加权图中的路径,同时重新访问属于循环的节点,并根据总行驶距离或从源节点的站点设置截止条件 据我所知,对于递归,深度优先搜索不需要显式堆栈结构,因此我想知道是否可以通过不使用显式堆栈来进一步简化下面的代码: public class DFSonWeightedDirectedGraph { private static final String START = "A"; private static final String END = "E";
public class DFSonWeightedDirectedGraph {
private static final String START = "A";
private static final String END = "E";
private int pathLength = 0;
private int stops = 0;
public static void main(String[] args) {
//this is a directed weighted graph
WeightedDirectedGraph graph = new WeightedDirectedGraph();
graph.addEdge("A", "B", 15);
graph.addEdge("A", "D", 15);
graph.addEdge("A", "E", 27);
//(...) more edges added
Stack<String> visited = new Stack<String>();
visited.push(START);
new DFSonWeightedDirectedGraph().depthFirst(graph, visited);
}
private void depthFirst(WeightedDirectedGraph graph, Stack<String> visited) {
Collection<Map.Entry<String, Integer>> tree_of_children
= graph.get_tree_of_children(visited.peek());
for (Map.Entry<String, Integer> child : tree_of_children) {
if(pathLength + child.getValue()>= 20){
continue;
}
visited.push(child.getKey());
pathLength += child.getValue();
stops += 1;
if (child.getKey().equals(END)) {
printPath(visited);
}
depthFirst(graph, visited);
visited.pop();
pathLength -= child.getValue();
stops -= 1;
}
}
private void printPath(Stack<String> visited) {
for (String node : visited) {
System.out.print(node);
System.out.print(" ");
}
System.out.println("[path length: "+pathLength +
" stops made: " + stops +"]");
}
}
公共类DFSonWeightedDirectedGraph{
私有静态最终字符串START=“A”;
私有静态最终字符串END=“E”;
私有int路径长度=0;
专用int停止=0;
公共静态void main(字符串[]args){
//这是一个有向加权图
WeightedDirectedGraph=新的WeightedDirectedGraph();
图.附录(“A”、“B”、15);
图.附录(“A”、“D”、15);
图.附录(“A”、“E”、27);
//(…)添加了更多边
堆栈访问=新堆栈();
已访问。推送(启动);
新建DFSonWeightedDirectedGraph().depthFirst(图形,已访问);
}
私有void depthFirst(加权DirectedGraph图,访问堆栈){
子集合树
=graph.get_tree_of_children(visted.peek());
for(Map.Entry子项:子项树){
if(pathLength+child.getValue()>=20){
继续;
}
push(child.getKey());
pathLength+=child.getValue();
停止次数+=1;
if(child.getKey().equals(END)){
printPath(已访问);
}
深度优先(图表,访问);
pop();
pathLength-=child.getValue();
停止-=1;
}
}
私有void打印路径(访问堆栈){
for(字符串节点:已访问){
系统输出打印(节点);
系统输出打印(“”);
}
System.out.println(“[路径长度:”+路径长度+
“停止:“+停止+”]”;
}
}
然而,其他没有显式堆栈结构的递归实现通常会考虑已经访问的节点,将它们着色为白色、灰色或黑色。那么,在我的例子中,如果允许重新访问,并且需要记录路径,那么绝对需要显式堆栈吗?感谢您对更简单的备选方案的建议。如果您必须保存路径,则需要为此提供数据结构。你的堆栈是好的;您可以用另一个数据结构替换它,但不能丢弃它
如果可以直接打印路径(而不是记录路径),则不需要堆栈。然后,您可以更改方法签名以仅获取图形和实际节点(可能还有实际路径长度和“停止点”)。编辑:此答案完全脱离主题,是基于对问题的误解而发布的 您的DFS实现存在一些问题。是的,它以深度优先的方式访问所有节点,并最终设法在开始和结束之间找到一条路径,但它不尝试检查已访问的节点,并且毫无理由地保留堆栈。在循环上不陷入无限递归的唯一原因是限制了最大路径长度,并且在所有顶点对之间有多条不同路径的图上仍然需要很长时间 使用堆栈的唯一目的是将要访问的节点传递到dfs函数旁边。您可以简单地去掉堆栈并直接传递节点 因此,与其
private void depthFirst(WeightedDirectedGraph graph, Stack<String> visited) {
...
visited.push(child);
...
depthFirst(graph, visited);
private void depthFirst(加权directedgraph图,访问堆栈){
...
参观、推(儿童);
...
深度优先(图表,访问);
你可以简单地把它写成
private void depthFirst(WeightedDirectedGraph graph, String node) {
...
//visited.push(child); <-- No longer needed
...
depthFirst(graph, child);
private void depthFirst(加权directedgraph图,字符串节点){
...
//visited.push(child);只需在节点结构中添加一个额外字段,即“visited”字段。这将是最快的。您必须在之后(或在执行搜索之前)取消标记所有节点
或者,在哈希表中散列节点的id。这将比堆栈更快检查。如果没有节点的id,最好创建一个id,以帮助调试、输出等
您确实需要额外的空间,但将布尔字段添加到每个节点将需要最少的空间,因为它将是每个节点1位,而堆栈中每个节点1个指针
您实际上不需要距离截止,因为您搜索的是有限图,每个节点只访问一次,所以在N节点图中最多访问N个节点。如果您搜索的是无限空间,例如在执行状态空间搜索时,则需要深度截止(例如,prolog解释器搜索证明).您不需要访问的节点。只需将当前子节点传递给递归方法,而不是访问的节点参数,并使用返回值来承载路径
如果可以逐个元素处理path元素,即重写printPath(),以便每个元素可以调用一次,则只需要将键类型作为返回类型。如果要接收整个路径,则需要将键值列表作为返回类型
实际上,您与解决方案的关系相对较近。只需使用递归方法调用的调用堆栈来表示路径。这非常有意义。因此,基本上的推理是,如果需要存储来保存路径以供进一步使用,则无论实现如何,都需要额外的空间来表示路径所在的问题上下文我使用的这种方法在我的文章中都提到过。我不需要跟踪访问的节点。跟踪访问的节点是DFS的一个重要部分(除非您有一个包含太多节点的图形需要管理),而不管程序的其余部分是否需要它。否则,您最终会遵循许多路径到达DFS
private void depthFirst(WeightedDirectedGraph graph, String node, Set<String> visited) {
visited.add(node); // mark current node as visited
...
//visited.push(child); <-- No longer needed
...
if (!visited.contains(child)){ // don't visit nodes we have worked on already
depthFirst(graph, child);
}