Open In App

Check if it is possible to finish all task from given dependencies (Course Schedule I)

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.

Recommended Practice

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 dependencies using 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:

Below is the implementation of the above approach:




// CPP program to check whether we can finish all
// tasks or not from given dependencies.
#include <bits/stdc++.h>
using namespace std;
 
// Returns adjacency list representation from a list
// of pairs.
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;
}
 
// A DFS based function to check if there is a cycle
// in the directed 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;
}
 
// Main function to check whether possible to finish all tasks or not
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;
 
    // for prerequisites: [[1, 0], [2, 1], [3, 2]]
 
    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 program to check whether we can finish all
// tasks or not from given dependencies.
import java.util.*;
 
public class GFG{
     
    // class to store dependencies as a pair
    static class pair{
        int first, second;
         
        pair(int first, int second){
            this.first = first;
            this.second = second;
        }
    }
     
    // Returns adjacency list representation from a list
    // of pairs.
    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;
    }
     
    // A DFS based function to check if there is a cycle
    // in the directed 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;
    }
     
    // Main function to check whether possible to finish all tasks or not
    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>();;
     
        // for prerequisites: [[1, 0], [2, 1], [3, 2]]
     
        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");
        }
    }
}
 
// This code is contributed by adityapande88.




// C# program to check whether we can finish all
// tasks or not from given dependencies.
using System;
using System.Collections.Generic;
 
public class GFG {
 
    // class to store dependencies as a pair
    public class pair {
        public int first, second;
        public pair(int first, int second)
        {
            this.first = first;
            this.second = second;
        }
    }
 
    // Returns adjacency list representation from a list
    // of pairs.
    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;
    }
 
    // A DFS based function to check if there is a cycle
    // in the directed 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;
        //onpath[node] = false;
        return false;
    }
 
    // Main function to check whether possible to finish all
    // tasks or not
    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>();
        ;
 
        // for prerequisites: [[1, 0], [2, 1], [3, 2]]
 
        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");
        }
    }
}
 
// This code is contributed by Abhijeet Kumar(abhijeet19403)




// Javascript program to check whether we can finish all
// tasks or not from given dependencies.
 
// Returns adjacency list representation from a list
// of pairs.
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;
}
 
// A DFS based function to check if there is a cycle
// in the directed 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;
}
 
// Main function to check whether possible to finish all
// tasks or not
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;
}
 
 
// Driver code to test above functions
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");
 
// This code is contributed by Nidhi goel




# Python3 program to check whether we can finish all
# tasks or not from given dependencies.
 
# class to store dependencies as a pair
class pair:
    def __init__(self, first, second):
        self.first = first
        self.second = second
 
# Returns adjacency list representation from a list
# of pairs.
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
 
# A DFS based function to check if there is a cycle
# in the directed 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
 
# Main function to check whether possible to finish all
# tasks or not
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
 
 
# Driver code to test above functions
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")
 
# This code is contributed by Abhijeet Kumar(abhijeet19403)

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+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:

Below is the implementation of the above approach:




// A BFS based solution to check if we can finish
// all tasks or not. This solution is mainly based
// on Kahn's algorithm.
#include <bits/stdc++.h>
using namespace std;
 
// Main function to check whether possible to
// finish all tasks or not
bool canFinish(int numTasks,
               vector<pair<int, int> >& prerequisites)
{
    vector<vector<int> > graph(numTasks);
    // vector to store the indegree of nodes
    vector<int> inDegree(numTasks, 0);
    // construct the graph and initialize the
    // indegree of nodes
    for (auto edge : prerequisites) {
        graph[edge.first].push_back(edge.second);
        inDegree[edge.second] += 1;
    }
    queue<int> q;
    // Push all the nodes with no dependencies
    // (indegree = 0)
    for (int i = 0; i < numTasks; i++) {
        if (!inDegree[i]) {
            q.push(i);
        }
    }
    while (!q.empty()) {
        int node = q.front();
        q.pop();
        // reduce the indegree of all neighbors by 1
        for (int child : graph[node]) {
            inDegree[child] -= 1;
            // Push the neighboring node if we have covered
            // all its dependencies (indegree = 0)
            if (!inDegree[child])
                q.push(child);
        }
    }
    // Check if there is a node whose indegree is not zero
    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;
}




import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
 
class Solution {
    // Main function to check whether it's possible to
    // finish all tasks or not
    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<>());
        }
 
        // Initialize the graph and in-degrees
        for (int[] edge : prerequisites) {
            graph.get(edge[0]).add(edge[1]);
            inDegree[edge[1]]++;
        }
 
        Queue<Integer> queue = new LinkedList<>();
 
        // Push all the nodes with no dependencies
        // (in-degree = 0) into the queue
        for (int i = 0; i < numTasks; i++) {
            if (inDegree[i] == 0) {
                queue.add(i);
            }
        }
 
        while (!queue.isEmpty()) {
            int node = queue.poll();
 
            // Reduce the in-degree of all neighbors by 1
            for (int neighbor : graph.get(node)) {
                inDegree[neighbor]--;
 
                // Push the neighboring node if we have
                // covered all its dependencies (in-degree =
                // 0)
                if (inDegree[neighbor] == 0) {
                    queue.add(neighbor);
                }
            }
        }
 
        // Check if there is a node whose in-degree is not
        // zero
        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");
        }
    }
}




using System;
using System.Collections.Generic;
 
class Program
{
    // Main function to check whether it is possible to
    // finish all tasks or not
    static bool CanFinish(int numTasks, List<Tuple<int, int>> prerequisites)
    {
        // Create a graph represented as an adjacency list
        List<List<int>> graph = new List<List<int>>(numTasks);
        for (int i = 0; i < numTasks; i++)
        {
            graph.Add(new List<int>());
        }
 
        // Array to store the indegree of nodes
        int[] inDegree = new int[numTasks];
 
        // Construct the graph and initialize the indegree of nodes
        foreach (var edge in prerequisites)
        {
            graph[edge.Item1].Add(edge.Item2);
            inDegree[edge.Item2]++;
        }
 
        // Queue to perform BFS
        Queue<int> queue = new Queue<int>();
 
        // Push all the nodes with no dependencies (indegree = 0)
        for (int i = 0; i < numTasks; i++)
        {
            if (inDegree[i] == 0)
            {
                queue.Enqueue(i);
            }
        }
 
        // Perform BFS
        while (queue.Count > 0)
        {
            int node = queue.Dequeue();
 
            // Reduce the indegree of all neighbors by 1
            foreach (int neighbor in graph[node])
            {
                inDegree[neighbor]--;
 
                // Enqueue the neighboring node if we have covered
                // all its dependencies (indegree = 0)
                if (inDegree[neighbor] == 0)
                {
                    queue.Enqueue(neighbor);
                }
            }
        }
 
        // Check if there is a node whose indegree is not zero
        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");
        }
    }
}




function canFinish(numTasks, prerequisites) {
    const graph = new Array(numTasks).fill().map(() => []);
    const inDegree = new Array(numTasks).fill(0);
 
    // Construct the graph and initialize the indegree of nodes
    prerequisites.forEach((edge) => {
        graph[edge[0]].push(edge[1]);
        inDegree[edge[1]] += 1;
    });
 
    const q = [];
 
    // Push all the nodes with no dependencies (indegree = 0)
    for (let i = 0; i < numTasks; i++) {
        if (inDegree[i] === 0) {
            q.push(i);
        }
    }
 
    while (q.length > 0) {
        const node = q.shift();
 
        // Reduce the indegree of all neighbors by 1
        for (const child of graph[node]) {
            inDegree[child] -= 1;
 
            // Push the neighboring node if we have covered
            // all its dependencies (indegree = 0)
            if (inDegree[child] === 0) {
                q.push(child);
            }
        }
    }
 
    // Check if there is a node whose indegree is not zero
    for (let i = 0; i < numTasks; i++) {
        if (inDegree[i] !== 0) {
            return false;
        }
    }
 
    return true;
}
 
// Main program
const numTasks = 4;
const prerequisites = [
    [1, 0],
    [2, 1],
    [3, 2]
];
 
if (canFinish(numTasks, prerequisites)) {
    console.log("Possible to finish all tasks");
} else {
    console.log("Impossible to finish all tasks");
}




from collections import deque
 
 
class Solution:
    # Main function to check whether it's
    # possible to finish all tasks or not
    def canFinish(self, numTasks, prerequisites):
        graph = [[] for _ in range(numTasks)]
        inDegree = [0] * numTasks
 
        # Initialize the graph and in-degrees
        for edge in prerequisites:
            graph[edge[0]].append(edge[1])
            inDegree[edge[1]] += 1
 
        queue = deque()
 
        # Push all the nodes with no dependencies
        # (in-degree = 0) into the queue
        for i in range(numTasks):
            if inDegree[i] == 0:
                queue.append(i)
 
        while queue:
            node = queue.popleft()
 
            # Reduce the in-degree of all neighbors by 1
            for neighbor in graph[node]:
                inDegree[neighbor] -= 1
 
                # Push the neighboring node if we have
                # covered all its dependencies (in-degree = 0)
                if inDegree[neighbor] == 0:
                    queue.append(neighbor)
 
        # Check if there is a node whose in-degree is not zero
        for i in range(numTasks):
            if inDegree[i] != 0:
                return False
 
        return True
 
 
# Test the function
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")

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)


Article Tags :