There are a total of N tasks, labeled from 0 to N-1. Some tasks may have prerequisites, for example to do task 0 you have to first complete task 1, which is expressed as a pair: [0, 1]. Given the total number of tasks N and a list of prerequisite pairs P, find if it is possible to finish all tasks.
Examples:
Input: N = 4, P = 3, prerequisites = {{1,0},{2,1},{3,2}}
Output: Yes
Explanation: To do task 1 you should have completed task 0, and to do task 2 you should have finished task 1, and to do task 3 you should have finished task 2. So it is possible.
Input: N = 2, P = 2, prerequisites = {{1,0},{0,1}}
Output: No
Explanation: To do task 1 you should have completed task 0, and to do task 0 you should have finished task 1. So it is impossible.
Asked In: Google, Twitter, Amazon and many more companies.
Solution:
We can consider this problem as a graph (related to topological sorting) problem. All tasks are nodes of the graph and if task u is a prerequisite of task v, we will add a directed edge from node u to node v. Now, this problem is equivalent to detecting a cycle in the graph represented by prerequisites. If there is a cycle in the graph, then it is not possible to finish all tasks (because in that case there is no any topological order of tasks). Both BFS and DFS can be used to solve it.
Since pair is inconvenient for the implementation of graph algorithms, we first transform it to a graph. If task u is a prerequisite of task v, we will add a directed edge from node u to node v.
Prerequisite: Detect Cycle in a Directed Graph
Check if it is possible to finish all task from given dependenciesusing DFS:
For DFS, it will first visit a node, then one neighbor of it, then one neighbor of this neighbor… and so on. If it meets a node which was visited in the current process of DFS visit, a cycle is detected and we will return false. Otherwise it will start from another unvisited node and repeat this process till all the nodes have been visited. Note that you should make two records: one is to record all the visited nodes and the other is to record the visited nodes in the current DFS visit.
Step by step approach:
- We use a vector visited to record all the visited nodes and another vector onpath to record the visited nodes of the current DFS visit.
- Start the DFS from a node which has not been visited once.
- During the DFS, if we encounter a node which was visited in the current DFS visit, then return false as we have got a cycle in the graph.
- Once the current visit is finished, we reset the onpath value of the nodes which were visited in the current DFS to false.
- If no cycle is found, return true.
Below is the implementation of the above approach:
CPP
#include <bits/stdc++.h>
using namespace std;
vector<unordered_set< int > > make_graph( int numTasks,
vector<pair< int , int > >& prerequisites)
{
vector<unordered_set< int > > graph(numTasks);
for ( auto pre : prerequisites)
graph[pre.second].insert(pre.first);
return graph;
}
bool dfs_cycle(vector<unordered_set< int > >& graph, int node,
vector< bool >& onpath, vector< bool >& visited)
{
if (visited[node])
return false ;
onpath[node] = visited[node] = true ;
for ( int neigh : graph[node])
if (onpath[neigh] || dfs_cycle(graph, neigh, onpath, visited))
return true ;
return onpath[node] = false ;
}
bool canFinish( int numTasks, vector<pair< int , int > >& prerequisites)
{
vector<unordered_set< int > > graph = make_graph(numTasks, prerequisites);
vector< bool > onpath(numTasks, false ), visited(numTasks, false );
for ( int i = 0; i < numTasks; i++)
if (!visited[i] && dfs_cycle(graph, i, onpath, visited))
return false ;
return true ;
}
int main()
{
int numTasks = 4;
vector<pair< int , int > > prerequisites;
prerequisites.push_back(make_pair(1, 0));
prerequisites.push_back(make_pair(2, 1));
prerequisites.push_back(make_pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
cout << "Possible to finish all tasks" ;
}
else {
cout << "Impossible to finish all tasks" ;
}
return 0;
}
|
Java
import java.util.*;
public class GFG{
static class pair{
int first, second;
pair( int first, int second){
this .first = first;
this .second = second;
}
}
static ArrayList<ArrayList<Integer>> make_graph( int numTasks,
Vector<pair> prerequisites)
{
ArrayList<ArrayList<Integer>> graph = new ArrayList<ArrayList<Integer>>(numTasks);
for ( int i= 0 ; i<numTasks; i++){
graph.add( new ArrayList<Integer>());
}
for (pair pre : prerequisites)
graph.get(pre.second).add(pre.first);
return graph;
}
static boolean dfs_cycle(ArrayList<ArrayList<Integer>> graph, int node,
boolean onpath[], boolean visited[])
{
if (visited[node])
return false ;
onpath[node] = visited[node] = true ;
for ( int neigh : graph.get(node))
if (onpath[neigh] || dfs_cycle(graph, neigh, onpath, visited))
return true ;
return onpath[node] = false ;
}
static boolean canFinish( int numTasks, Vector<pair> prerequisites)
{
ArrayList<ArrayList<Integer>> graph = make_graph(numTasks, prerequisites);
boolean onpath[] = new boolean [numTasks];
boolean visited[] = new boolean [numTasks];
for ( int i = 0 ; i < numTasks; i++)
if (!visited[i] && dfs_cycle(graph, i, onpath, visited))
return false ;
return true ;
}
public static void main(String args[])
{
int numTasks = 4 ;
Vector<pair> prerequisites = new Vector<pair>();;
prerequisites.add( new pair( 1 , 0 ));
prerequisites.add( new pair( 2 , 1 ));
prerequisites.add( new pair( 3 , 2 ));
if (canFinish(numTasks, prerequisites)) {
System.out.println( "Possible to finish all tasks" );
}
else {
System.out.println( "Impossible to finish all tasks" );
}
}
}
|
Python3
class pair:
def __init__( self , first, second):
self .first = first
self .second = second
def make_graph(numTasks, prerequisites):
graph = []
for i in range (numTasks):
graph.append([])
for pre in prerequisites:
graph[pre.second].append(pre.first)
return graph
def dfs_cycle(graph, node, onpath, visited):
if visited[node]:
return false
onpath[node] = visited[node] = True
for neigh in graph[node]:
if (onpath[neigh] or dfs_cycle(graph, neigh, onpath, visited)):
return true
return False
def canFinish(numTasks, prerequisites):
graph = make_graph(numTasks, prerequisites)
onpath = [ False ] * numTasks
visited = [ False ] * numTasks
for i in range (numTasks):
if ( not visited[i] and dfs_cycle(graph, i, onpath, visited)):
return False
return True
numTasks = 4
prerequisites = []
prerequisites.append(pair( 1 , 0 ))
prerequisites.append(pair( 2 , 1 ))
prerequisites.append(pair( 3 , 2 ))
if canFinish(numTasks, prerequisites):
print ( "Possible to finish all tasks" )
else :
print ( "Impossible to finish all tasks" )
|
C#
using System;
using System.Collections.Generic;
public class GFG {
public class pair {
public int first, second;
public pair( int first, int second)
{
this .first = first;
this .second = second;
}
}
static List<List< int > >
make_graph( int numTasks, List<pair> prerequisites)
{
List<List< int > > graph
= new List<List< int > >(numTasks);
for ( int i = 0; i < numTasks; i++) {
graph.Add( new List< int >());
}
foreach (pair pre in prerequisites) graph[pre.second]
.Add(pre.first);
return graph;
}
static bool dfs_cycle(List<List< int > > graph, int node,
bool [] onpath, bool [] visited)
{
if (visited[node])
return false ;
onpath[node] = visited[node] = true ;
foreach ( int neigh in graph[node])
if (onpath[neigh] || dfs_cycle(graph, neigh, onpath,
visited))
return true ;
return false ;
}
static bool canFinish( int numTasks,
List<pair> prerequisites)
{
List<List< int > > graph
= make_graph(numTasks, prerequisites);
bool [] onpath = new bool [numTasks];
bool [] visited = new bool [numTasks];
for ( int i = 0; i < numTasks; i++)
if (!visited[i]
&& dfs_cycle(graph, i, onpath, visited))
return false ;
return true ;
}
public static void Main(String[] args)
{
int numTasks = 4;
List<pair> prerequisites = new List<pair>();
;
prerequisites.Add( new pair(1, 0));
prerequisites.Add( new pair(2, 1));
prerequisites.Add( new pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
Console.WriteLine(
"Possible to finish all tasks" );
}
else {
Console.WriteLine(
"Impossible to finish all tasks" );
}
}
}
|
Javascript
function make_graph(numTasks, prerequisites){
let graph = [];
for (let i = 0; i < numTasks; i++){
graph.push([]);
}
for (let i = 0; i < prerequisites.length; i++){
graph[prerequisites[i][1]].push(prerequisites[i][0]);
}
return graph;
}
function dfs_cycle(graph, node, onpath, visited){
if (visited[node])
return false ;
onpath[node] = visited[node] = true ;
for (let i = 0; i < graph[node].length; i++){
let neigh = graph[node][i];
if (onpath[neigh] == true || dfs_cycle(graph, neigh, onpath, visited) == true )
return true ;
}
return false ;
}
function canFinish(numTasks, prerequisites){
let graph = make_graph(numTasks, prerequisites);
let onpath = new Array(numTasks).fill( false );
let visited = new Array(numTasks).fill( false );
for (let i = 0; i < numTasks; i++){
if (visited[i] == false && dfs_cycle(graph, i, onpath, visited ) == true ){
return false ;
}
}
return true ;
}
let numTasks = 4;
let prerequisites = [];
prerequisites.push([1, 0]);
prerequisites.push([2, 1]);
prerequisites.push([3, 2]);
if (canFinish(numTasks, prerequisites))
console.log( "Possible to finish all tasks" );
else
console.log( "Impossible to finish all tasks" );
|
Output
Possible to finish all tasks
Time Complexity: O(V*(V+E)), where V is the number of vertices and E is the number of edges.
Auxiliary Space: O(V+E)
Check if it is possible to finish all task from given dependencies using Topological Sort:
If we perform a topological sort and all the nodes get visited, then it means there is no cycle and it is possible to finish all the tasks. If any of the node is unvisited, it means that particular node is dependent on another node which in turn is dependent on some other node and so on. So, we cannot finish those tasks because of circular dependency.
Step by step approach:
- Calculate the indegree of all the nodes of the graph.
- Start by pushing all the nodes with indegree 0 into a queue and start traversing by popping elements from the queue.
- Whenever we pop a node, we decrease indegree of all its neighboring nodes by 1 and if indegree of any of the neighboring nodes becomes 0, then we push that neighboring node into our queue.
- Continue the above steps till our queue becomes empty.
- Now, check if all the nodes are visited. If yes, return true, else return false.
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
bool canFinish( int numTasks,
vector<pair< int , int > >& prerequisites)
{
vector<vector< int > > graph(numTasks);
vector< int > inDegree(numTasks, 0);
for ( auto edge : prerequisites) {
graph[edge.first].push_back(edge.second);
inDegree[edge.second] += 1;
}
queue< int > q;
for ( int i = 0; i < numTasks; i++) {
if (!inDegree[i]) {
q.push(i);
}
}
while (!q.empty()) {
int node = q.front();
q.pop();
for ( int child : graph[node]) {
inDegree[child] -= 1;
if (!inDegree[child])
q.push(child);
}
}
for ( int i = 0; i < numTasks; i++) {
if (inDegree[i])
return false ;
}
return true ;
}
int main()
{
int numTasks = 4;
vector<pair< int , int > > prerequisites;
prerequisites.push_back(make_pair(1, 0));
prerequisites.push_back(make_pair(2, 1));
prerequisites.push_back(make_pair(3, 2));
if (canFinish(numTasks, prerequisites)) {
cout << "Possible to finish all tasks" ;
}
else {
cout << "Impossible to finish all tasks" ;
}
return 0;
}
|
Java
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
class Solution {
public boolean canFinish( int numTasks,
int [][] prerequisites)
{
ArrayList<ArrayList<Integer> > graph
= new ArrayList<>(numTasks);
int [] inDegree = new int [numTasks];
for ( int i = 0 ; i < numTasks; i++) {
graph.add( new ArrayList<>());
}
for ( int [] edge : prerequisites) {
graph.get(edge[ 0 ]).add(edge[ 1 ]);
inDegree[edge[ 1 ]]++;
}
Queue<Integer> queue = new LinkedList<>();
for ( int i = 0 ; i < numTasks; i++) {
if (inDegree[i] == 0 ) {
queue.add(i);
}
}
while (!queue.isEmpty()) {
int node = queue.poll();
for ( int neighbor : graph.get(node)) {
inDegree[neighbor]--;
if (inDegree[neighbor] == 0 ) {
queue.add(neighbor);
}
}
}
for ( int i = 0 ; i < numTasks; i++) {
if (inDegree[i] != 0 ) {
return false ;
}
}
return true ;
}
public static void main(String[] args)
{
int numTasks = 4 ;
int [][] prerequisites
= { { 1 , 0 }, { 2 , 1 }, { 3 , 2 } };
Solution solution = new Solution();
if (solution.canFinish(numTasks, prerequisites)) {
System.out.println(
"Possible to finish all tasks" );
}
else {
System.out.println(
"Impossible to finish all tasks" );
}
}
}
|
Python3
from collections import deque
class Solution:
def canFinish( self , numTasks, prerequisites):
graph = [[] for _ in range (numTasks)]
inDegree = [ 0 ] * numTasks
for edge in prerequisites:
graph[edge[ 0 ]].append(edge[ 1 ])
inDegree[edge[ 1 ]] + = 1
queue = deque()
for i in range (numTasks):
if inDegree[i] = = 0 :
queue.append(i)
while queue:
node = queue.popleft()
for neighbor in graph[node]:
inDegree[neighbor] - = 1
if inDegree[neighbor] = = 0 :
queue.append(neighbor)
for i in range (numTasks):
if inDegree[i] ! = 0 :
return False
return True
numTasks = 4
prerequisites = [
[ 1 , 0 ],
[ 2 , 1 ],
[ 3 , 2 ]
]
solution = Solution()
if solution.canFinish(numTasks, prerequisites):
print ( "Possible to finish all tasks" )
else :
print ( "Impossible to finish all tasks" )
|
C#
using System;
using System.Collections.Generic;
class Program
{
static bool CanFinish( int numTasks, List<Tuple< int , int >> prerequisites)
{
List<List< int >> graph = new List<List< int >>(numTasks);
for ( int i = 0; i < numTasks; i++)
{
graph.Add( new List< int >());
}
int [] inDegree = new int [numTasks];
foreach ( var edge in prerequisites)
{
graph[edge.Item1].Add(edge.Item2);
inDegree[edge.Item2]++;
}
Queue< int > queue = new Queue< int >();
for ( int i = 0; i < numTasks; i++)
{
if (inDegree[i] == 0)
{
queue.Enqueue(i);
}
}
while (queue.Count > 0)
{
int node = queue.Dequeue();
foreach ( int neighbor in graph[node])
{
inDegree[neighbor]--;
if (inDegree[neighbor] == 0)
{
queue.Enqueue(neighbor);
}
}
}
for ( int i = 0; i < numTasks; i++)
{
if (inDegree[i] > 0)
{
return false ;
}
}
return true ;
}
static void Main()
{
int numTasks = 4;
List<Tuple< int , int >> prerequisites = new List<Tuple< int , int >>();
prerequisites.Add( new Tuple< int , int >(1, 0));
prerequisites.Add( new Tuple< int , int >(2, 1));
prerequisites.Add( new Tuple< int , int >(3, 2));
if (CanFinish(numTasks, prerequisites))
{
Console.WriteLine( "Possible to finish all tasks" );
}
else
{
Console.WriteLine( "Impossible to finish all tasks" );
}
}
}
|
Output
Possible to finish all tasks
Time Complexity: O(V+E), where V is the number of vertices and E is the number of edges.
Auxiliary Space: O(V)
Feeling lost in the world of random DSA topics, wasting time without progress? It's time for a change! Join our DSA course, where we'll guide you on an exciting journey to master DSA efficiently and on schedule.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 geeks!
Last Updated :
30 Nov, 2023
Like Article
Save Article