使用dfs在图中查找单个路径

时间:2014-12-17 20:17:21

标签: java recursion matrix path depth-first-search

我目前正在尝试在从源到接收器的图表中找到单个路径。我正在尝试使用dfs实现一个方法来实现这一点。但是,我似乎无法弄清楚如何使方法停止递归。例如,我有这个图(矩阵形式)

0 1 1 0

0 0 0 1

0 0 0 1

0 0 0 0

所以我有一个从节点0(源)到节点1和2的边缘,然后从1到2的边缘到3(接收器)。我想要的路径是0> 1> 3,而是我得到0> 1> 3> 2> 3。一旦找到了接收器的路径,我怎样才能使递归停止?

以下是该方法的代码:

public void dfsPath(int i) {

    boolean[] visited = new boolean[this.edgeCapacities.length];
    visited[i] = true;
    this.path.add(i); //Integer ArrayList containing the nodes in the path

            //loop through all of the nodes in the matrix to find adjacency
            for (int j = 0; j < this.edgeCapacities.length; j++) {
                //check if edge exists and node has not been visited
                if (this.edgeCapacities[i][j] != 0 && !visited[j]) {
                    //here is the problem, i want the recursion to stop once the sink is found
                    //it does not work however.
                    if(j == this.sink) {
                        visited[j] = true;
                        this.path.add(j);
                        return;
                    } else {
                        //recursion
                        dfsPath(j);
                    }
                }
        }

非常感谢任何帮助。提前谢谢。

1 个答案:

答案 0 :(得分:1)

您的DFS算法似乎存在一些问题:

  • 通过在每个递归调用中创建一个新的visited列表,它始终只包含当前节点
  • 您只是向this.path添加节点,但从未删除未达到目标的节点
  • 你永远不会检查你的一个递归调用是否达到目标,从而将更多节点添加到一个完美的路径

要解决此问题,您应该在方法结束时从this.path删除当前节点,即如果没有找到路径。此外,您可以删除visited数组,只检查下一个节点是否已在路径中。这不是那么快,但应该足以满足您的需求,并使代码不那么复杂。此外,该方法应返回truefalse,具体取决于是否找到路径。

尝试此操作(未经过测试,但应该工作)。

public boolean dfsPath(int i) {
    this.path.add(i); // add current node to path
    if (i == this.sink) {
        return true; // if current node is sink, return true
                     // this.path contains nodes from source to sink
    }
    for (int j = 0; j < this.edgeCapacities.length; j++) {
        if (this.edgeCapacities[i][j] != 0 && ! this.path.contains(j)) {
            if (dfsPath(j)) {
                return true; // found a path -> search no further
            }
        }
    }
    this.path.remove(this.path.size() - 1); // pop last node
    return false; // no path found
}

请注意,我还移动了sink - 检查了循环。这纯粹是一种品味问题,但它使代码更简单,因为您不必将sink节点分别添加到路径中。