
Problem Statement
In this problem, you are given a directed graph represented as a list of edges, where each edge edges[i] = [ai, bi]
shows a direct link from node ai
to node bi
. Besides the graph itself, you are provided two specific nodes: source
and destination
. Your task is to determine if every possible path starting from the source
node ultimately leads exclusively to the destination
node under the following conditions:
- There exists at least one valid path from the
source
to thedestination
. - Any potential path that leads to a node with no outward edges must terminate at the
destination
node. - The total number of different paths from the
source
to thedestination
is finite.
The solution should return true
if all paths from the source
adhere to these conditions leading to the destination
node, otherwise false
.
Examples
Example 1
Input:
n = 3, edges = [[0,1],[0,2]], source = 0, destination = 2
Output:
false
Explanation:
It is possible to reach and get stuck on both node 1 and node 2.
Example 2
Input:
n = 4, edges = [[0,1],[0,3],[1,2],[2,1]], source = 0, destination = 3
Output:
false
Explanation:
We have two possibilities: to end at node 3, or to loop over node 1 and node 2 indefinitely.
Example 3
Input:
n = 4, edges = [[0,1],[0,2],[1,3],[2,3]], source = 0, destination = 3
Output:
true
Constraints
1 <= n <= 104
0 <= edges.length <= 104
edges.length == 2
0 <= ai, bi <= n - 1
0 <= source <= n - 1
0 <= destination <= n - 1
- The given graph may have self-loops and parallel edges.
Approach and Intuition
Based on the provided examples and constraints, we can derive an approach to solve the task:
Graph Representation: Start by representing the graph with an adjacency list. This enables easy traversal and checking outgoing edges for each node.
Depth-First Search (DFS) or Breadth-First Search (BFS):
- Use DFS or BFS to explore all possible paths from the
source
. This helps in understanding which nodes are accessible from thesource
and also to detect cycles. - During this traversal, also check nodes that have no outgoing edges. Ensure these nodes are the
destination
node. If any such node isn't, immediately returnfalse
.
- Use DFS or BFS to explore all possible paths from the
Cycle Detection:
- Detect cycles in the paths using a visited stack. If a cycle is detected and it doesn't include the
destination
node or it prevents reaching thedestination
, returnfalse
. - If
destination
is part of a cycle, it must be the only node in its cycle or have a direct cycle (self-loop), such that the traversal is confined only todestination
.
- Detect cycles in the paths using a visited stack. If a cycle is detected and it doesn't include the
Path Confirmation:
- Ensure every reachable node from the
source
has paths leading solely to thedestination
. If any node can lead to an alternate path not concluding atdestination
, or traps in a cyclic loop not involvingdestination
, the condition is not satisfied.
- Ensure every reachable node from the
Validation:
- After traversal, if all paths lead from
source
todestination
without inconsistencies like unreachable destination or incorrect terminations, then confirm by returningtrue
.
- After traversal, if all paths lead from
Complexity:
The complexity mainly depends on the number of nodes (n
) and edges in the graph given the need to traverse potentially all nodes and edges. With the constraints provided, we ensure the approach remains efficient within these limits.
Solutions
- Java
- Python
class Solution {
enum State { VISITING, VISITED };
public boolean canReachDestination(int number_of_nodes, int[][] links, int start, int target) {
List<Integer>[] adjacencyList = createGraph(number_of_nodes, links);
return checkPath(adjacencyList, start, target, new State[number_of_nodes]);
}
private boolean checkPath(List<Integer>[] adjList, int current, int end, State[] visited) {
if (visited[current] != null) {
return visited[current] == State.VISITED;
}
if (adjList[current].isEmpty()) {
return current == end;
}
visited[current] = State.VISITING;
for (int neighbor : adjList[current]) {
if (!checkPath(adjList, neighbor, end, visited)) {
return false;
}
}
visited[current] = State.VISITED;
return true;
}
private List<Integer>[] createGraph(int nodes, int[][] connection) {
List<Integer>[] resultGraph = new List[nodes];
for (int i = 0; i < nodes; i++) {
resultGraph[i] = new ArrayList<>();
}
for (int[] link : connection) {
resultGraph[link[0]].add(link[1]);
}
return resultGraph;
}
}
The given Java code defines a method to determine whether all possible paths from a specified source node lead to a designated destination node in a directed graph. This is performed within the Solution
class, utilizing a depth-first search (DFS) algorithm enhanced with state tracking to manage node visits.
Key features of the solution:
Graph Representation and Initialization: A graph is represented using an adjacency list, where each node index contains a list of direct connections to other nodes. The
createGraph
method initializes this representation based on the input number of nodes and their connections.State Tracking: Nodes in the graph are tracked using an enumeration
State
with valuesVISITING
andVISITED
, to prevent revisits and detect cycles or paths that might not reach the target.Path Checking: The
checkPath
method implements the core DFS logic. If a node is already visited and labeled asVISITED
, it confirms a valid path to the target already exists through this route. Nodes without outgoing links are check ends, directly comparing the current node to the target. During the traversal, it marks nodes asVISITING
and upon completion of all possible routes from that node, it marks them asVISITED
.Overall Handling: The
canReachDestination
method serves as the entry point, where it creates the adjacency list for the graph, initializes the state array for nodes, and triggers the path checking from the source node to the target.
The code provides scalability and efficiency in determining valid paths in a graph structure using systematic state transitions and depth-first traversal, making it suitable for problems dealing with route validations in directed graphs.
class Solution:
IN_PROGRESS = 1
COMPLETED = 2
def hasPath(self, node_count: int, connections: List[List[int]], start: int, end: int) -> bool:
adjacency_list = self.createGraph(node_count, connections)
return self.checkPath(adjacency_list, start, end, [None] * node_count)
def checkPath(self, graph, current_node, target, visited_states):
if visited_states[current_node] is not None:
return visited_states[current_node] == Solution.COMPLETED
if not graph[current_node]:
return current_node == target
visited_states[current_node] = Solution.IN_PROGRESS
for neighbor in graph[current_node]:
if not self.checkPath(graph, neighbor, target, visited_states):
return False
visited_states[current_node] = Solution.COMPLETED
return True
def createGraph(self, total_nodes, links):
adj_list = [[] for _ in range(total_nodes)]
for link in links:
adj_list[link[0]].append(link[1])
return adj_list
The Python3 program features the Solution
class designed to determine if all paths starting from a source node can consistently lead to a destination node in a directed graph. The algorithm is structured around three key functions:
hasPath
: Initializes the graph from the input and invokescheckPath
to explore possible routes from the source to the target.checkPath
: Recursively explores each path and checks for cycles using a state array, ensuring paths do not re-enter 'in-progress' nodes.createGraph
: Constructs an adjacency list from the input connections to represent the graph.
The solution employs depth-first search (DFS) for path traversal, conducted primarily in the checkPath
function. Here's a succinct overview of its approach:
- The graph is first converted into an adjacency list to facilitate efficient traversal of connections.
- A recursive DFS is guided by the
checkPath
function, which employs a three-state system (None, IN_PROGRESS, COMPLETED) stored invisited_states
to manage the exploration status of each node:- None indicates the node hasn't been visited.
- IN_PROGRESS marks nodes currently being explored, assisting in cycle detection.
- COMPLETED denotes nodes fully processed without finding invalid paths.
- During the DFS, if a node has no outgoing edges, it checks if the node is the destination. If it's currently being explored and found again, a cycle is detected, and
False
is immediately returned. - The process continues for all neighbors of the node; if any path check returns
False
, the function propagates this to prevent further unnecessary checks.
This method ensures that all explorations are not only efficient but also robust against cycles and incomplete paths. The efficient backtracking and state management allow the function to handle complex graphs effectively without revisiting or reprocessing completed nodes. This program thus offers a reliable way to assess path completeness in directed graphs from a given start to end node.
No comments yet.