简体   繁体   中英

Depth-first/breadth-first algorithm printing all nodes; how do I get it to only print nodes in the path?

I have an adjacency matrix adj of the form below:

0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 1 0 0 0 
1 0 0 0 1 0 1 0 0 
0 0 0 1 0 1 0 0 0 
0 0 1 0 1 0 0 0 1 
0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 1 0 0 0 

This is the adjacency matrix for a maze with rules adj(x,y) = 1 if:

  1. x != y
  2. x is adjacent to y
  3. neither x or y is a wall in the maze

The maze is as below (beside it are the element numbers):

S X E | 1 2 3
O O O | 4 5 6
O X O | 7 8 9
//S = starting position
//E = ending position
//X = wall

I have a DFS algorithm that will display the nodes to traverse from S to E , but it displays unnecessary nodes.

public static void main(String [] args){
    int[][] adj = //the adjacency matrix
    boolean[] visited = new boolean[adj.length];
    int n = adj.length;    
    int m = 1; //starting position
    int o = 3; //ending position
    DFS(adjMatrix, visited, n, m, o);
}

public static void DFS(int[][] adj, boolean[] visited, int n, int i, int o){
    System.out.print(" " + (i+1));
    visited[i]= true;
    if (i+1 != o) {
        for (int j = 0; j<n;j++){
            if(!(visited[j]) && adj[i][j]==1){
               DFS(adj, visited, n, j, o);
            }
        }
    }
}

public static void BFS(int[][] adj, boolean[] visited, int n, int i, int o){
    queue Q = new queue;
    visited[i]= true;
    Q.enqueue(i);
    while (!Q.isEmpty()) {
        //...
    }
}

This prints 1 4 5 6 3 9 7 . I'm wracking my head around modifying it so that it will only print 1 4 5 6 3 .

What have I done wrong here?

There are some major issues with the code, in addition to fixes needed for the DFS algorithm:

  • You Start and end are wrong: it should be decreased by 1 (because the indices are 0 based)
  • Your adjanecy matrix is wrong (it is of size 10X9 - it should be a squared matrix) (edit fixed it)
  • Your solution should only print elements that are in the path. One way to do it would be to return a List<> (rather than void - that populates all the nodes in the current path. If you reached the destination, create the list, otherwise - return null . Attach elements only when the recursive call returns something that is not null . Code attached

Also note, it prints the nodes in the correct order (and not reversed order)

public static void main(String [] args){
    int[][] adj = {
            {0, 0, 0, 1, 0, 0, 0, 0, 0}, 
            {0, 0, 0, 0, 0, 0, 0, 0, 0}, 
            {0, 0, 0, 0, 0, 1, 0, 0, 0}, 
            {1, 0, 0, 0, 1, 0, 1, 0, 0}, 
            {0, 0, 0, 1, 0, 1, 0, 0, 0}, 
            {0, 0, 1, 0, 1, 0, 0, 0, 1}, 
            {0, 0, 0, 1, 0, 0, 0, 0, 0}, 
            {0, 0, 0, 0, 0, 0, 0, 0, 0}, 
            { 0, 0, 0, 0, 0, 1, 0, 0, 0} 
    };
    boolean[] visited = new boolean[adj.length];
    int n = adj.length;    
    int m = 1-1; //starting position
    int o = 3-1; //ending position
    System.out.println(DFS(adj, visited, n, m, o));
}

public static List<Integer> DFS(int[][] adj, boolean[] visited, int n, int i, int o){
    visited[i]= true;
    if (i == o) return new LinkedList<Integer>(Arrays.asList(i+1));
    for (int j = 0; j<n;j++){
        if(!(visited[j]) && adj[i][j]==1){
            List<Integer> res = DFS(adj, visited, n, j, o);
            if (res != null) { 
                res.add(0, i+1);
                return res;
            }
        }
    }
    return null; //no path
}

Will result (as expected) with:

[1, 4, 5, 6, 3]

As a side note, though this solution is complete (will always find a solution if such exists), it is not optimal - it might return a longer solution than the shortest one.

If you want to find the shortest path from source to target, consider switching to a BFS

Try with this code:

public static boolean DFS(int[][] adj, boolean[] visited, int n, int i, int o){        
    visited[i]= true;
    boolean good = false;
    if (i+1 != o) {
        for (int j = 0; j<n;j++){
            if(!(visited[j]) && adj[i][j]==1){
               good |= DFS(adj, visited, n, j, o);
            }
        }
    } else {
        good = true;
    }
    if (good) System.out.print(" " + (i+1));
    return good;
}

This will print paths in reverse (from end to start) - but it will only print nodes that are part of a good path. If you need to print the path in start-to-end order, you can store it in an array and then print it in reverse:

public static void DFS(int[][] adj, boolean[] visited, 
       ArrayList<int> path, int n, int i, int o){        
    visited[i]= true;
    if (i+1 != o) {
        for (int j = 0; j<n;j++){
            if(!(visited[j]) && adj[i][j]==1){
               path.add(j);
               DFS(adj, visited, n, j, o);
               path.remove(path.size()-1);
            }
        }
    } else {
        // show path
        for (int i : path) {
            System.out.print(" " + i);
        }
    }        
}

When you finally reach your destination, the method stack will have the path.

  ArrayList<Integer> list = new ArrayList<>(); // this will have your path.
  public static boolean DFS(int[][] adj, boolean[] visited, int n, int i, int o){
    if(i==o){
       list.add(o);
      //System.out.println(i);
      return true;
    }
    visited[i]= true;
    for (int j = 0; j<n;j++){
       if(!(visited[j]) && adj[i][j]==1){
          if(DFS(adj, visited, n, j, o)){
              list.add(0,j);
              //System.out.println(j);
              return true;
           }
         }
     }
   return false;
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM