Skip to main content

Finish Time of Tasks II - Solution & Explanation

HardPremiumFree on FleetCode6 min read
Practice this problem

Problem Statement

You are given an integer n representing the number of tasks in a project, numbered from 0 to n - 1. These tasks are connected as an undirected tree. This is represented by a 2D integer array edges of length n - 1, where edges[i] = [ui, vi] indicates an undirected connection between task ui and task vi.

You are also given an array baseTime of length n, where baseTime[i] represents the time to complete task i.

For any chosen task as the root, the finish time of each task is calculated as follows:

  • Leaf task: The finish time is baseTime[i].
  • Non-leaf task:
    • Let earliest be the minimum finish time among its children, and latest be the maximum finish time among its children.
    • Let ownDuration be (latest - earliest) + baseTime[i].
    • Finish time of task i is latest + ownDuration.

Choose any task as the root and compute the finish time of that root based on the rules above.

Return the minimum possible finish time among all choices of root.

 

Example 1:

Input: n = 3, edges = [[0,1],[1,2]], baseTime = [9,1,5]

Output: 14

Explanation:

0 9 1 1 2 5

The optimal choice is to treat task 1 as the root.

  • Task 0 is a leaf, so its finish time is baseTime[0] = 9.
  • Task 2 is a leaf, so its finish time is baseTime[2] = 5.
  • Task 1 has two children with finish times 9 and 5:
    • earliest = 5, latest = 9
    • ownDuration = (latest - earliest) + baseTime[1] = (9 - 5) + 1 = 5
    • Finish time of task 1 is latest + ownDuration = 9 + 5 = 14

Thus, the minimum possible finish time among all choices of root is 14.

Example 2:

Input: n = 3, edges = [[0,1],[0,2]], baseTime = [4,7,6]

Output: 12

Explanation:

0 4 1 7 2 6

The optimal choice is to treat task 0 as the root.

  • Task 1 is a leaf, so its finish time is baseTime[1] = 7.
  • Task 2 is a leaf, so its finish time is baseTime[2] = 6.
  • Task 0 has two children with finish times 7 and 6:
    • earliest = 6, latest = 7
    • ownDuration = (latest - earliest) + baseTime[0] = (7 - 6) + 4 = 5
    • Finish time of task 0 is latest + ownDuration = 7 + 5 = 12

Thus, the minimum possible finish time among all choices of root is 12.

Example 3:

Input: n = 4, edges = [[0,1],[0,2],[2,3]], baseTime = [5,8,2,1]

Output: 16

Explanation:

0 5 1 8 2 2 3 1

The optimal choice is to treat task 1 as the root.

  • Task 3 is a leaf, so its finish time is baseTime[3] = 1.
  • Task 2 has one child task 3:
    • earliest = latest = 1
    • ownDuration = (latest - earliest) + baseTime[2] = 0 + 2 = 2
    • Finish time of task 2 is latest + ownDuration = 1 + 2 = 3
  • Task 0 has one child task 2:
    • earliest = latest = 3
    • ownDuration = (latest - earliest) + baseTime[0] = 0 + 5 = 5
    • Finish time of task 0 is latest + ownDuration = 3 + 5 = 8
  • Task 1 has one child task 0:
    • earliest = latest = 8
    • ownDuration = (latest - earliest) + baseTime[1] = 0 + 8 = 8
    • Finish time of task 1 is latest + ownDuration = 8 + 8 = 16

Thus, the minimum possible finish time among all choices of root is 16.

 

Constraints:

  • 1 <= n <= 105
  • edges.length = n - 1
  • edges[i] == [ui, vi]
  • 0 <= ui, vi <= n - 1
  • ui != vi
  • The input is generated such that edges represents a valid undirected tree.
  • baseTime.length == n
  • 1 <= baseTime[i] <= 105

Approach Overview

Problem Overview: You are given multiple tasks where some tasks depend on others. Each task can start only after all its prerequisite tasks finish. The goal is to determine the final completion time considering these dependencies and task durations.

Approach 1: Brute Force Dependency Simulation (High Time Complexity)

A direct strategy repeatedly scans all tasks and checks whether their prerequisites have finished. If every dependency of a task is completed, the task can start and its finish time becomes start + duration. The algorithm keeps updating finish times until no further updates occur. This works but performs many redundant checks across the dependency list. With n tasks and m dependencies, repeated scans can push the time complexity toward O(n * m) with O(n) space. It mainly helps build intuition about dependency resolution.

Approach 2: Topological Sort + Dynamic Programming (Optimal, O(V+E))

The task dependency graph forms a Directed Acyclic Graph (DAG). A natural solution is to process tasks in topological order so every prerequisite is handled before the dependent task. Build an adjacency list and track indegree counts for each node. Start with tasks that have indegree = 0. For each processed task, update its neighbors by computing the earliest finish time using finish[next] = max(finish[next], finish[curr] + duration[next]). Push neighbors into the queue when their indegree becomes zero. This guarantees each edge and node is processed once, giving O(V + E) time and O(V + E) space. The key insight is that the finish time of a task equals the longest path ending at that node in the DAG.

Approach 3: DFS + Memoization (Longest Path in DAG)

Another way to compute completion times is depth‑first search with memoization. For every task, recursively compute the maximum completion time of all prerequisite paths. Cache the computed finish time so each node is evaluated once. This transforms the problem into a longest‑path calculation in a DAG. With memoization and adjacency lists, the complexity remains O(V + E) time and O(V) recursion/memo space. This approach is concise but recursion depth can be a limitation for very large graphs.

Recommended for interviews: The expected solution is topological sorting combined with dynamic programming. It clearly models dependency ordering and computes earliest completion times in one pass. Interviewers often like to see the brute force idea first to demonstrate understanding of the dependency constraints, then the optimized DAG approach using graph traversal and topological sort. The finish‑time update step is essentially a longest‑path calculation on a DAG, which also connects to dynamic programming concepts.

Solutions for this problem are being prepared.

Try solving it yourself

Detailed Complexity Analysis

ApproachTimeSpaceWhen to Use
Brute Force Dependency SimulationO(n * m)O(n)Understanding the dependency process; small input sizes
Topological Sort + DPO(V + E)O(V + E)General case for DAG scheduling; interview‑preferred solution
DFS + Memoization (Longest Path)O(V + E)O(V)When recursion is convenient for DAG longest‑path problems

Frequently Asked Questions

Is Finish Time of Tasks II easy or hard?
Finish Time of Tasks II is typically classified as a Hard problem because it combines multiple concepts: graph modeling, topological sorting, and dynamic programming on DAGs. Understanding how to propagate maximum completion times along dependency edges is the key challenge.
Finish Time of Tasks II Python/Java solution
Most implementations follow the same structure: build the adjacency list, compute indegrees, run a queue‑based topological sort, and update finish times using dynamic programming. Python commonly uses collections.deque, while Java implementations use ArrayList for the graph and ArrayDeque or LinkedList for the queue.
How to solve Finish Time of Tasks II in O(n)?
Model tasks and dependencies as a directed acyclic graph. Perform a topological sort using a queue and indegree array. While processing nodes, update the earliest finish time for dependent tasks. This ensures each task and dependency is processed once, achieving linear O(V + E) complexity.
What is the best approach for Finish Time of Tasks II?
Topological sorting combined with dynamic programming is the most efficient approach. Process tasks in dependency order using a queue and maintain the earliest finish time for each task. For every edge u → v, update finish[v] = max(finish[v], finish[u] + duration[v]). The algorithm runs in O(V + E) time.
Is Finish Time of Tasks II asked at Google/Amazon/Meta?
Problems involving task scheduling with dependencies and DAG longest‑path calculations are common in interviews at companies like Google, Amazon, and Meta. Variants frequently appear in system scheduling and graph algorithm interview rounds.
What data structure is used in Finish Time of Tasks II?
The typical solution uses an adjacency list to represent the dependency graph, an indegree array to support topological sorting, and a queue for BFS processing. A DP array stores the earliest finish time for each task.
What is the time complexity of Finish Time of Tasks II?
The optimal solution runs in O(V + E) time where V is the number of tasks and E is the number of dependency relations. Each node enters the queue once and every edge is processed exactly once during the topological traversal.

Ready to solve this problem?

Practice Finish Time of Tasks II with our built-in code editor and test cases.

Practice on FleetCode