Given a Directed Acyclic Graph (DAG) with V vertices (numbered 0 to V-1) and a list of directed edges edges[][], where each edges[i] = [u, v] represents a directed edge from vertex u to vertex v. Given two vertices src and dest, find the total number of distinct paths from src to dest.
Note: The graph has no self-loops or multiple edges.
Examples:
Input: V = 5, edges[][] = [[0, 1], [0, 2], [0, 4], [1, 3], [1, 4], [2, 4], [3, 2]], src = 0, dest = 4 Output: 4 Explanation: There are 4 paths from 0 to 4. 0 -> 4 0 -> 1 -> 4 0 -> 2 -> 4 0 -> 1 -> 3 -> 2 -> 4
Input: V = 4, edges[][] = [[0, 1], [0, 3], [1, 2], [1, 3], [2, 3]], src = 0, dest = 3 Output: 3 Explanation: There are 3 paths from 0 to 3. 0 -> 3 0 -> 1 -> 3 0 -> 1 -> 2 -> 3
Consider above graph, with src = 0 and dest = 4, nodes 1, 2, and 3 form a cycle (1 -> 2 -> 3 -> 1), and 3 also has an exit edge to 4. At 3, the path can either exit to 4 or loop back through the cycle first.
This gives 0 -> 1 -> 2 -> 3 -> 4 (no loop), 0 -> 1 -> 2 -> 3 -> 1 -> 2 -> 3 -> 4 (one loop), and so on indefinitely - each loop produces one more valid path. So the path count is infinite whenever a cycle offers a repeatable choice to loop or exit toward dest, which is exactly why the problem requires the graph to be acyclic.
[Naive Approach] Using DFS Traversal - O(2 ^ V) Time and O(V) Space
The idea is to start a DFS from src and explore all possible paths to dest. Whenever dest is reached, count it as one valid path. Since the graph is a DAG, a vertex can be reached through multiple different paths, causing the same subproblems to be recomputed many times. As a result, the number of recursive calls can grow exponentially in the number of vertices.
Step by Step Implementation:
Build an adjacency list from the given edges.
Start a DFS traversal from src.
If the current vertex is dest, return 1.
Recursively explore all outgoing neighbors.
Sum the number of paths returned by each recursive call.
[Expected Approach 1] Using DFS and Memoization - O(V + E) Time and O(V + E) Space
The idea is to use DFS to count the number of paths from a vertex to dest. Since the graph is a DAG, the number of paths from a vertex remains the same whenever it is revisited. Therefore, we store the computed result for each vertex in a visited array and reuse it whenever needed. This avoids recomputing the same subproblems multiple times and allows each vertex to be processed only once.
Step By Step Implementation:
Build an adjacency list from the given edges.
Create a visited array initialized with -1 to store computed path counts.
Start a DFS traversal from src.
If the current vertex is dest, return 1.
If the result for the current vertex is already computed, return it.
Recursively count paths through all outgoing neighbors.
Store the computed count in the visited array.
Return the number of paths from src to dest.
C++
#include<bits/stdc++.h>usingnamespacestd;intdfs(intu,intdest,vector<vector<int>>&adj,vector<int>&visited){if(u==dest)return1;if(visited[u]!=-1)returnvisited[u];inttotal=0;for(intv:adj[u]){total+=dfs(v,dest,adj,visited);}returnvisited[u]=total;}intcountPaths(intV,vector<vector<int>>&edges,intsrc,intdest){vector<vector<int>>adj(V);for(auto&e:edges)adj[e[0]].push_back(e[1]);// visited[u] stores the number of paths from u to dest, once computedvector<int>visited(V,-1);returndfs(src,dest,adj,visited);}intmain(){intV=5;vector<vector<int>>edges={{0,1},{0,2},{0,4},{1,3},{1,4},{2,4},{3,2}};intsrc=0,dest=4;cout<<countPaths(V,edges,src,dest)<<endl;return0;}
Java
importjava.util.ArrayList;importjava.util.List;importjava.util.Arrays;classGFG{staticintdfs(intu,intdest,List<List<Integer>>adj,int[]visited){if(u==dest)return1;if(visited[u]!=-1)returnvisited[u];inttotal=0;for(intv:adj.get(u)){total+=dfs(v,dest,adj,visited);}visited[u]=total;returntotal;}staticintcountPaths(intV,int[][]edges,intsrc,intdest){List<List<Integer>>adj=newArrayList<>();for(inti=0;i<V;i++)adj.add(newArrayList<>());for(int[]e:edges)adj.get(e[0]).add(e[1]);// visited[u] stores the number of paths from u to dest, once computedint[]visited=newint[V];Arrays.fill(visited,-1);returndfs(src,dest,adj,visited);}publicstaticvoidmain(String[]args){intV=5;int[][]edges={{0,1},{0,2},{0,4},{1,3},{1,4},{2,4},{3,2}};intsrc=0,dest=4;System.out.println(countPaths(V,edges,src,dest));}}
Python
defdfs(u,dest,adj,visited):ifu==dest:return1ifvisited[u]!=-1:returnvisited[u]total=0forvinadj[u]:total+=dfs(v,dest,adj,visited)visited[u]=totalreturntotaldefcountPaths(V,edges,src,dest):adj=[[]for_inrange(V)]foru,vinedges:adj[u].append(v)# visited[u] stores the number of paths from u to dest, once computedvisited=[-1]*Vreturndfs(src,dest,adj,visited)V=5edges=[[0,1],[0,2],[0,4],[1,3],[1,4],[2,4],[3,2]]src,dest=0,4print(countPaths(V,edges,src,dest))
C#
usingSystem;usingSystem.Collections.Generic;classGFG{staticintDfs(intu,intdest,List<int>[]adj,int[]visited){if(u==dest)return1;if(visited[u]!=-1)returnvisited[u];inttotal=0;foreach(intvinadj[u]){total+=Dfs(v,dest,adj,visited);}visited[u]=total;returntotal;}staticintcountPaths(intV,int[][]edges,intsrc,intdest){List<int>[]adj=newList<int>[V];for(inti=0;i<V;i++)adj[i]=newList<int>();foreach(int[]einedges)adj[e[0]].Add(e[1]);// visited[u] stores the number of paths from u to dest, once computedint[]visited=newint[V];for(inti=0;i<V;i++)visited[i]=-1;returnDfs(src,dest,adj,visited);}staticvoidMain(){intV=5;int[][]edges={newint[]{0,1},newint[]{0,2},newint[]{0,4},newint[]{1,3},newint[]{1,4},newint[]{2,4},newint[]{3,2}};intsrc=0,dest=4;Console.WriteLine(countPaths(V,edges,src,dest));}}
JavaScript
functiondfs(u,dest,adj,visited){if(u===dest)return1;if(visited[u]!==-1)returnvisited[u];lettotal=0;for(constvofadj[u]){total+=dfs(v,dest,adj,visited);}visited[u]=total;returntotal;}functioncountPaths(V,edges,src,dest){constadj=Array.from({length:V},()=>[]);for(const[u,v]ofedges)adj[u].push(v);// visited[u] stores the number of paths from u to dest, once computedconstvisited=newArray(V).fill(-1);returndfs(src,dest,adj,visited);}// Driver CodeconstV=5;constedges=[[0,1],[0,2],[0,4],[1,3],[1,4],[2,4],[3,2]];constsrc=0,dest=4;console.log(countPaths(V,edges,src,dest));
Output
4
[Expected Approach 2] Using Topological Sort and Dynamic Programming - O(V + E) Time and O(V + E) Space
The idea is to process the vertices in topological order. Since all edges in a DAG go from an earlier vertex to a later vertex in the topological ordering, the number of paths from a vertex can be computed using the path counts of its outgoing neighbors. We initialize the destination vertex with one path to itself and propagate path counts in reverse topological order to compute the answer for all vertices.
Step by Step Implementation:
Build the adjacency list and compute the indegree of every vertex.
Use Kahn's Algorithm to obtain a topological ordering of the vertices.
Create a DP array and initialize dp[dest] = 1.
Traverse the topological order in reverse.
For each vertex, add the path counts of all its outgoing neighbors.
Store the result in the DP array.
Return dp[src] as the total number of paths from src to dest.