Given a Directed Acyclic Graph (DAG) with V jobs (numbered from 1 to V) and E dependency edges[][], where each edge [u, v] indicates that job v can start only after job u has been completed, find the earliest completion time of every job. Each job takes exactly one unit of time to complete, and any number of independent jobs can be executed simultaneously. Return an array of size V, where the elements represent the earliest completion times of jobs 1 to V in order.
Examples:
Input: V = 10, E = 13, edges[][] = [[1, 3], [1, 4], [1, 5], [2, 4], [2, 8], [2, 9], [3, 6], [4, 6], [4, 8], [5, 8], [6, 7], [7, 8], [8, 10]] Output: [1, 1, 2, 2, 2, 3, 4, 5, 2, 6] Explanation: Jobs 1 and 2 have no prerequisites, so they complete at time 1. Jobs 3, 4, 5, and 9 can start only after their prerequisites are completed, so they complete at time 2. Job 6 depends on jobs 3 and 4, so it completes at time 3. Job 7 depends on job 6, so it completes at time 4. Job 8 depends on jobs 2, 4, 5, and 7. Since job 7 finishes last (at time 4), job 8 completes at time 5. Job 10 depends on job 8, so it completes at time 6.
Input: V = 7, E = 7, edges[][] = [[1, 2], [2, 3], [2, 4], [2, 5], [3, 6], [4, 6], [5, 7]] Output: [1, 2, 3, 3, 3, 4, 4] Explanation: Job 1 has no prerequisite, so it completes at time 1. Job 2 depends on job 1, so it completes at time 2. Jobs 3, 4, and 5 depend on job 2, so they complete at time 3. Job 6 depends on jobs 3 and 4, so it completes at time 4. Job 7 depends on job 5, so it also completes at time 4.
[Naive Approach] Using DFS from Every Job - O(V × (V + E)) Time and O(V + E) Space
The idea is to build a reverse graph (u->v is stored as v->u). Once we build reverse graph, it becomes easy to perform a DFS for every job to find the maximum completion time among all its prerequisite jobs..The completion time of a job is 1 + the maximum completion time of its prerequisites. Since DFS is repeated for every job, many computations are performed multiple times.
Working of the Approach:
First, construct a reverse graph where for every dependency u → v, an edge v → u is stored. This allows us to directly access all prerequisite jobs of any job.
For each job, perform a DFS on the reverse graph. If a job has no prerequisites, it can start immediately, so its completion time is 1.
While traversing the prerequisites, recursively compute their earliest completion times and keep track of the maximum among them, since the current job must wait for all its prerequisites to finish.
Finally, assign the current job's completion time as 1 + maximum completion time of its prerequisites. Repeating this for every job gives the earliest completion time of all jobs.
C++
#include<algorithm>#include<iostream>#include<vector>usingnamespacestd;// Returns the earliest completion time of the current job.intdfs(intnode,vector<vector<int>>&revGraph){// No prerequisite jobs.if(revGraph[node].empty())return1;intmxTime=0;// Find the maximum completion time among all prerequisites.for(intprev:revGraph[node])mxTime=max(mxTime,dfs(prev,revGraph));returnmxTime+1;}vector<int>minTime(intV,vector<vector<int>>&edges){// Build the reverse graph.vector<vector<int>>revGraph(V);for(auto&edge:edges){intu=edge[0]-1;intv=edge[1]-1;revGraph[v].push_back(u);}// Stores the earliest completion time of every job.vector<int>ans(V);// Compute the completion time for every job.for(inti=0;i<V;i++)ans[i]=dfs(i,revGraph);returnans;}intmain(){intV=10;vector<vector<int>>edges={{1,3},{1,4},{1,5},{2,4},{2,8},{2,9},{3,6},{4,6},{4,8},{5,8},{6,7},{7,8},{8,10}};vector<int>ans=minTime(V,edges);cout<<"[";for(inti=0;i<ans.size();i++){cout<<ans[i];if(i!=ans.size()-1)cout<<", ";}cout<<"]";return0;}
Java
importjava.util.ArrayList;publicclassGFG{// Returns the earliest completion time of the current// job.staticintdfs(intnode,ArrayList<ArrayList<Integer>>revGraph){// No prerequisite jobs.if(revGraph.get(node).isEmpty())return1;intmxTime=0;// Find the maximum completion time among all// prerequisites.for(intprev:revGraph.get(node))mxTime=Math.max(mxTime,dfs(prev,revGraph));returnmxTime+1;}staticArrayList<Integer>minTime(intV,int[][]edges){// Build the reverse graph.ArrayList<ArrayList<Integer>>revGraph=newArrayList<>();for(inti=0;i<V;i++)revGraph.add(newArrayList<>());for(int[]edge:edges){intu=edge[0]-1;intv=edge[1]-1;revGraph.get(v).add(u);}// Stores the earliest completion time of every job.ArrayList<Integer>ans=newArrayList<>();// Compute the completion time for every job.for(inti=0;i<V;i++)ans.add(dfs(i,revGraph));returnans;}publicstaticvoidmain(String[]args){intV=10;int[][]edges={{1,3},{1,4},{1,5},{2,4},{2,8},{2,9},{3,6},{4,6},{4,8},{5,8},{6,7},{7,8},{8,10}};ArrayList<Integer>ans=minTime(V,edges);System.out.print("[");for(inti=0;i<ans.size();i++){System.out.print(ans.get(i));if(i!=ans.size()-1)System.out.print(", ");}System.out.print("]");}}
Python
# Returns the earliest completion time of the current job.defdfs(node,revGraph):# No prerequisite jobs.ifnotrevGraph[node]:return1mxTime=0# Find the maximum completion time among all prerequisites.forprevinrevGraph[node]:mxTime=max(mxTime,dfs(prev,revGraph))returnmxTime+1defminimumTime(V,edges):# Build the reverse graph.revGraph=[[]for_inrange(V)]foru,vinedges:revGraph[v-1].append(u-1)# Stores the earliest completion time of every job.ans=[]# Compute the completion time for every job.foriinrange(V):ans.append(dfs(i,revGraph))returnansif__name__=="__main__":V=10edges=[[1,3],[1,4],[1,5],[2,4],[2,8],[2,9],[3,6],[4,6],[4,8],[5,8],[6,7],[7,8],[8,10]]ans=minimumTime(V,edges)print(ans)
C#
usingSystem;usingSystem.Collections.Generic;classGFG{// Returns the earliest completion time of the current// job.staticintDfs(intnode,List<List<int>>revGraph){// No prerequisite jobs.if(revGraph[node].Count==0)return1;intmxTime=0;// Find the maximum completion time among all// prerequisites.foreach(intprevinrevGraph[node])mxTime=Math.Max(mxTime,Dfs(prev,revGraph));returnmxTime+1;}staticList<int>minTime(intV,int[,]edges){// Build the reverse graph.List<List<int>>revGraph=newList<List<int>>();for(inti=0;i<V;i++)revGraph.Add(newList<int>());intE=edges.GetLength(0);for(inti=0;i<E;i++){intu=edges[i,0]-1;intv=edges[i,1]-1;revGraph[v].Add(u);}// Stores the earliest completion time of every job.List<int>ans=newList<int>();// Compute the completion time for every job.for(inti=0;i<V;i++)ans.Add(Dfs(i,revGraph));returnans;}staticvoidMain(){intV=10;int[,]edges={{1,3},{1,4},{1,5},{2,4},{2,8},{2,9},{3,6},{4,6},{4,8},{5,8},{6,7},{7,8},{8,10}};List<int>ans=minTime(V,edges);Console.Write("[");for(inti=0;i<ans.Count;i++){Console.Write(ans[i]);if(i!=ans.Count-1)Console.Write(", ");}Console.Write("]");}}
JavaScript
functiondfs(node,revGraph){// No prerequisite jobs.if(revGraph[node].length===0)return1;letmxTime=0;// Find the maximum completion time among all// prerequisites.for(letprevofrevGraph[node])mxTime=Math.max(mxTime,dfs(prev,revGraph));returnmxTime+1;}functionminTime(V,edges){// Build the reverse graph.letrevGraph=Array.from({length:V},()=>[]);for(letedgeofedges){letu=edge[0]-1;letv=edge[1]-1;revGraph[v].push(u);}// Stores the earliest completion time of every job.letans=Array(V).fill(0);// Compute the completion time for every job.for(leti=0;i<V;i++)ans[i]=dfs(i,revGraph);returnans;}// Driver CodeletV=10;letedges=[[1,3],[1,4],[1,5],[2,4],[2,8],[2,9],[3,6],[4,6],[4,8],[5,8],[6,7],[7,8],[8,10]];letans=minTime(V,edges);console.log("[");for(leti=0;i<ans.length;i++){process.stdout.write(ans[i].toString());if(i!==ans.length-1)process.stdout.write(", ");}console.log("]");
Output
[1, 1, 2, 2, 2, 3, 4, 5, 2, 6]
[Expected Approach] Using Topological Sorting (Kahn's Algorithm) - O(V + E) Time and O(V + E) Space
The idea is to process the jobs in topological order using Kahn's Algorithm. Jobs with no prerequisites are assigned a completion time of 1. As each job is processed, its dependent jobs are updated, and once all prerequisites of a job are completed, its completion time is assigned as current job's completion time + 1. This processes every job and edge only once.
Let us understand with an example: Input: V = 10, E = 13, edges[][] = [[1, 3], [1, 4], [1, 5], [2, 4], [2, 8], [2, 9], [3, 6], [4, 6], [4, 8], [5, 8], [6, 7], [7, 8], [8, 10]]
Build the graph and compute the indegree of every job. Jobs 1 and 2 have indegree 0, so they are added to the queue and assigned completion time 1.
Process jobs 1 and 2 first. After all their prerequisites are satisfied, jobs 3, 4, 5, and 9 become ready and are assigned completion time 2.
Next, process jobs 3 and 4. Once both are completed, job 6 has no remaining prerequisites and gets completion time 3.
Processing job 6 makes job 7 ready with completion time 4. After processing job 7, job 8 becomes ready and gets completion time 5.
Finally, processing job 8 makes job 10 ready, so its completion time becomes 6. The final answer is [1, 1, 2, 2, 2, 3, 4, 5, 2, 6].
C++
#include<algorithm>#include<iostream>#include<queue>#include<vector>usingnamespacestd;// Compute the earliest completion time of every job.voidfindTime(intV,vector<int>graph[],vector<int>&time,vector<int>&indegree){queue<int>q;// Jobs with no dependencies finish at time 1.for(inti=0;i<V;i++){if(indegree[i]==0){q.push(i);time[i]=1;}}// Process jobs in topological order.while(!q.empty()){intcurr=q.front();q.pop();// Visit all dependent jobs.for(intnext:graph[curr]){indegree[next]--;// All prerequisites of this job are completed.if(indegree[next]==0){time[next]=time[curr]+1;q.push(next);}}}}vector<int>minTime(intV,vector<vector<int>>&edges){// Build the graph and compute indegrees.vector<int>graph[V],indegree(V,0);for(auto&edge:edges){intu=edge[0]-1;intv=edge[1]-1;graph[u].push_back(v);indegree[v]++;}// Stores the earliest completion time of each job.vector<int>time(V);findTime(V,graph,time,indegree);returntime;}intmain(){intV=10;vector<vector<int>>edges={{1,3},{1,4},{1,5},{2,4},{2,8},{2,9},{3,6},{4,6},{4,8},{5,8},{6,7},{7,8},{8,10}};vector<int>ans=minTime(V,edges);cout<<"[";for(inti=0;i<ans.size();i++){cout<<ans[i];if(i!=ans.size()-1)cout<<", ";}cout<<"]";return0;}
Java
importjava.util.ArrayList;importjava.util.LinkedList;importjava.util.Queue;publicclassGFG{// Compute the earliest completion time of every job.staticvoidfindTime(intV,ArrayList<ArrayList<Integer>>graph,int[]time,int[]indegree){Queue<Integer>q=newLinkedList<>();// Jobs with no dependencies finish at time 1.for(inti=0;i<V;i++){if(indegree[i]==0){q.offer(i);time[i]=1;}}// Process jobs in topological order.while(!q.isEmpty()){intcurr=q.poll();// Visit all dependent jobs.for(intnext:graph.get(curr)){indegree[next]--;// All prerequisites of this job are// completed.if(indegree[next]==0){time[next]=time[curr]+1;q.offer(next);}}}}staticArrayList<Integer>minTime(intV,int[][]edges){// Build the graph and compute indegrees.ArrayList<ArrayList<Integer>>graph=newArrayList<>();for(inti=0;i<V;i++)graph.add(newArrayList<>());int[]indegree=newint[V];for(int[]edge:edges){intu=edge[0]-1;intv=edge[1]-1;graph.get(u).add(v);indegree[v]++;}// Stores the earliest completion time of each job.int[]time=newint[V];findTime(V,graph,time,indegree);ArrayList<Integer>ans=newArrayList<>();for(intx:time)ans.add(x);returnans;}publicstaticvoidmain(String[]args){intV=10;int[][]edges={{1,3},{1,4},{1,5},{2,4},{2,8},{2,9},{3,6},{4,6},{4,8},{5,8},{6,7},{7,8},{8,10}};ArrayList<Integer>ans=minTime(V,edges);System.out.print("[");for(inti=0;i<ans.size();i++){System.out.print(ans.get(i));if(i!=ans.size()-1)System.out.print(", ");}System.out.print("]");}}
Python
fromcollectionsimportdeque# Compute the earliest completion time of every job.deffindTime(V,graph,time,indegree):q=deque()# Jobs with no dependencies finish at time 1.foriinrange(V):ifindegree[i]==0:q.append(i)time[i]=1# Process jobs in topological order.whileq:curr=q.popleft()# Visit all dependent jobs.fornextingraph[curr]:indegree[next]-=1# All prerequisites of this job are completed.ifindegree[next]==0:time[next]=time[curr]+1q.append(next)defminTime(V,edges):# Build the graph and compute indegrees.graph=[[]for_inrange(V)]indegree=[0]*Vforedgeinedges:u=edge[0]-1v=edge[1]-1graph[u].append(v)indegree[v]+=1# Stores the earliest completion time of each job.time=[0]*VfindTime(V,graph,time,indegree)returntimeif__name__=="__main__":V=10edges=[[1,3],[1,4],[1,5],[2,4],[2,8],[2,9],[3,6],[4,6],[4,8],[5,8],[6,7],[7,8],[8,10]]ans=minTime(V,edges)print('[')foriinrange(len(ans)):print(ans[i],end='')ifi!=len(ans)-1:print(', ',end='')print(']')
C#
usingSystem;usingSystem.Collections.Generic;classGFG{// Compute the earliest completion time of every job.staticvoidFindTime(intV,List<int>[]graph,int[]time,int[]indegree){Queue<int>q=newQueue<int>();// Jobs with no dependencies finish at time 1.for(inti=0;i<V;i++){if(indegree[i]==0){q.Enqueue(i);time[i]=1;}}// Process jobs in topological order.while(q.Count>0){intcurr=q.Dequeue();// Visit all dependent jobs.foreach(intnextingraph[curr]){indegree[next]--;// All prerequisites of this job are// completed.if(indegree[next]==0){time[next]=time[curr]+1;q.Enqueue(next);}}}}staticList<int>minTime(intV,int[,]edges){// Build the graph and compute indegrees.List<int>[]graph=newList<int>[V];for(inti=0;i<V;i++)graph[i]=newList<int>();int[]indegree=newint[V];intm=edges.GetLength(0);for(inti=0;i<m;i++){intu=edges[i,0]-1;intv=edges[i,1]-1;graph[u].Add(v);indegree[v]++;}// Stores the earliest completion time of each job.int[]time=newint[V];FindTime(V,graph,time,indegree);returnnewList<int>(time);}staticvoidMain(){intV=10;int[,]edges={{1,3},{1,4},{1,5},{2,4},{2,8},{2,9},{3,6},{4,6},{4,8},{5,8},{6,7},{7,8},{8,10}};List<int>ans=minTime(V,edges);Console.Write("[");for(inti=0;i<ans.Count;i++){Console.Write(ans[i]);if(i!=ans.Count-1)Console.Write(", ");}Console.Write("]");}}
JavaScript
// Compute the earliest completion time of every job.functionfindTime(V,graph,time,indegree){letq=[];// Jobs with no dependencies finish at time 1.for(leti=0;i<V;i++){if(indegree[i]===0){q.push(i);time[i]=1;}}// Process jobs in topological order.while(q.length>0){letcurr=q.shift();// Visit all dependent jobs.for(letnextofgraph[curr]){indegree[next]--;// All prerequisites of this job are completed.if(indegree[next]===0){time[next]=time[curr]+1;q.push(next);}}}}functionminTime(V,edges){// Build the graph and compute indegrees.letgraph=Array.from({length:V},()=>[]);letindegree=Array(V).fill(0);for(letedgeofedges){letu=edge[0]-1;letv=edge[1]-1;graph[u].push(v);indegree[v]++;}// Stores the earliest completion time of each job.lettime=Array(V).fill(0);findTime(V,graph,time,indegree);returntime;}// Driver CodeletV=10;letedges=[[1,3],[1,4],[1,5],[2,4],[2,8],[2,9],[3,6],[4,6],[4,8],[5,8],[6,7],[7,8],[8,10]];letans=minTime(V,edges);console.log("[");for(leti=0;i<ans.length;i++){console.log(ans[i]);if(i!==ans.length-1){process.stdout.write(", ");}}console.log("]");