Cracking the Shortest Path Problem in Graph Theory

April 8, 2024

Graph theory problems often appear in coding interviews, testing both our understanding of algorithms and our ability to translate theory into code. One such problem involves finding the shortest path between nodes in a graph represented as a series of strings. Here's a breakdown of how to tackle this challenge using Java.

The Problem Statement

You are given an array of strings, strArr, that models a non-looping graph. The structure of strArr is as follows:

  1. The first element is the number of nodes N.
  2. The next N elements are the nodes themselves, which can be any strings like "A", "B", "Brick Street", etc.
  3. The remaining elements are connections between these nodes in the form of "Node1-Node2". The graph is undirected, meaning "Node1-Node2" implies both directions are possible.

Your task is to find the shortest path from the first node to the last node and return it as a string separated by dashes. If no path exists, return -1.

Approach to Solve the Problem

To solve this problem, we can employ the Breadth-First Search (BFS) algorithm, which is well-suited for finding the shortest path in an unweighted graph. Here’s a step-by-step approach:

1. Parse the Input

First, extract the number of nodes, the list of nodes, and the list of connections from the strArr array.

import java.util.*;

public class ShortestPath {

    public static String ShortestPath(String[] strArr) {
        int N = Integer.parseInt(strArr[0]);
        List<String> nodes = Arrays.asList(Arrays.copyOfRange(strArr, 1, N + 1));
        List<String> connections = Arrays.asList(Arrays.copyOfRange(strArr, N + 1, strArr.length));

        Map<String, List<String>> graph = buildGraph(nodes, connections);
        return bfs(nodes.get(0), nodes.get(N - 1), graph);
    }

    private static Map<String, List<String>> buildGraph(List<String> nodes, List<String> connections) {
        Map<String, List<String>> graph = new HashMap<>();
        for (String node : nodes) {
            graph.put(node, new ArrayList<>());
        }
        for (String connection : connections) {
            String[] edge = connection.split("-");
            graph.get(edge[0]).add(edge[1]);
            graph.get(edge[1]).add(edge[0]);
        }
        return graph;
    }

2. Implement BFS for Shortest Path

BFS will help in finding the shortest path efficiently. We use a queue to explore nodes level by level and a map to track the paths.

    private static String bfs(String start, String end, Map<String, List<String>> graph) {
        if (start.equals(end)) return start;
        Queue<String> queue = new LinkedList<>();
        Map<String, String> cameFrom = new HashMap<>();
        queue.add(start);
        cameFrom.put(start, null);

        while (!queue.isEmpty()) {
            String current = queue.poll();
            for (String neighbor : graph.get(current)) {
                if (!cameFrom.containsKey(neighbor)) {
                    queue.add(neighbor);
                    cameFrom.put(neighbor, current);
                    if (neighbor.equals(end)) {
                        return reconstructPath(cameFrom, end);
                    }
                }
            }
        }
        return "-1";
    }

    private static String reconstructPath(Map<String, String> cameFrom, String end) {
        List<String> path = new ArrayList<>();
        for (String at = end; at != null; at = cameFrom.get(at)) {
            path.add(at);
        }
        Collections.reverse(path);
        return String.join("-", path);
    }
}

3. Explanation of the Code

  • Graph Construction: We create an adjacency list to represent the graph using a map where each node points to its neighbors.
  • BFS Traversal: We use a queue to perform the BFS, starting from the source node. We track the path using a cameFrom map that records the predecessor of each node.
  • Path Reconstruction: Once the destination node is reached, we backtrack using the cameFrom map to construct the path.

Conclusion

Finding the shortest path in a graph is a fundamental problem in computer science, and using BFS is an effective way to solve it for unweighted graphs. By breaking down the problem into manageable steps and implementing a clear solution, you can tackle similar graph theory challenges with confidence.


This approach ensures clarity and efficiency, allowing us to solve the shortest path problem effectively.