3683. Earliest Time to Finish One Task
Problem Description
You are given a 2D integer array tasks, where each element tasks[i] = [sᵢ, tᵢ] describes a single task:
sᵢis the start time of the task.tᵢis the duration, i.e., how many units of time the task takes to complete.
A task that starts at time sᵢ and runs for tᵢ units of time will be finished at time sᵢ + tᵢ.
Your goal is to find the earliest moment at which at least one task is finished. In other words, among all tasks, compute each task's finish time sᵢ + tᵢ and return the smallest value.
Example:
Suppose tasks = [[1, 5], [3, 2], [4, 4]]:
- Task 0 finishes at
1 + 5 = 6 - Task 1 finishes at
3 + 2 = 5 - Task 2 finishes at
4 + 4 = 8
The earliest finish time is 5, so the answer is 5.
How We Pick the Algorithm
Why Simulation / Basic DSA?
This problem maps to Simulation / Basic DSA through a short path in the full flowchart.
The problem computes the earliest finish time by simply adding each task's start time and duration, then taking the minimum across all tasks.
Open in FlowchartIntuition
The key observation is that each task is completely independent — tasks don't interact with each other, and there are no constraints like shared resources or ordering between them.
This means the finish time of any task i is fully determined by its own values: it starts at sᵢ and takes tᵢ units of time, so it finishes exactly at sᵢ + tᵢ.
Since we want the earliest time at which any one task is done, the problem reduces to a simple question: among all the finish times sᵢ + tᵢ, which one is the smallest?
There's no need for sorting, simulation, or any complex data structure — a single pass over the array, computing sᵢ + tᵢ for each task and keeping track of the minimum, directly gives the answer.
Solution Approach
Single Pass
Following the reference approach, we iterate through the tasks array exactly once. For each task [sᵢ, tᵢ], we compute its completion time as sᵢ + tᵢ, and we keep track of the minimum value seen so far. After processing all tasks, that minimum is the earliest time at which at least one task is finished.
Steps:
- For each task
[s, t]intasks, compute the finish times + t. - Track the smallest finish time across all tasks.
- Return that smallest value.
In Python, this can be expressed concisely using a generator expression combined with the built-in min function:
class Solution:
def earliestTime(self, tasks: List[List[int]]) -> int:
return min(s + t for s, t in tasks)
How the code works:
for s, t in tasksunpacks each task into its start timesand durationt.s + tproduces the finish time of that task.min(...)consumes the generator and returns the smallest finish time, which is exactly the answer.
Complexity Analysis:
- Time complexity:
O(n), wherenis the number of tasks — each task is visited exactly once. - Space complexity:
O(1)— the generator expression evaluates lazily, so no extra array is built; only a constant amount of state is maintained.
Example Walkthrough
Let's trace through the solution with tasks = [[2, 3], [5, 1], [1, 8], [4, 2]].
We perform a single pass over the array, computing each task's finish time s + t and updating the minimum seen so far:
| Step | Task [s, t] | Finish time s + t | Minimum so far |
|---|---|---|---|
| 1 | [2, 3] | 2 + 3 = 5 | 5 |
| 2 | [5, 1] | 5 + 1 = 6 | 5 (6 > 5, no update) |
| 3 | [1, 8] | 1 + 8 = 9 | 5 (9 > 5, no update) |
| 4 | [4, 2] | 4 + 2 = 6 | 5 (6 > 5, no update) |
After processing all four tasks, the smallest finish time recorded is 5, so the answer is 5.
A few points worth noticing from this trace:
- The task that finishes first (
[2, 3]) is neither the one that starts earliest ([1, 8]) nor the one with the shortest duration ([5, 1]). Only the sums + tmatters, which is why we can't shortcut by looking at start times or durations alone. - Each task is examined exactly once and no ordering between tasks is needed, confirming the
O(n)time andO(1)space behavior of the single-pass approach.
In code, min(s + t for s, t in tasks) performs exactly this process: it lazily yields 5, 6, 9, 6 and returns the smallest value, 5.
Solution Implementation
1from typing import List
2
3
4class Solution:
5 def earliestTime(self, tasks: List[List[int]]) -> int:
6 # Each task is represented as [start_time, duration].
7 # A task started at time s with duration t finishes at time s + t.
8 # The earliest time any task can be completed is the minimum
9 # finishing time across all tasks.
10 return min(start_time + duration for start_time, duration in tasks)
11```
12
13**Explanation of the approach:**
14
151. **Problem intent**: Each task `[s, t]` begins at time `s` and takes `t` units of time, so it finishes at `s + t`. We want the earliest possible completion time among all tasks.
16
172. **Step-by-step reasoning**:
18 - Unpack each task into `start_time` and `duration` for readability.
19 - Compute the finish time `start_time + duration` for every task using a generator expression (memory-efficient, no intermediate list).
20 - Apply `min()` to obtain the smallest finish time.
21
223. **Complexity**:
23 - Time: **O(n)** — a single pass through all tasks.
24 - Space: **O(1)** — the generator expression avoids building an extra list.
25
26**Alternative perspective** — an explicit loop version, which some may find clearer for debugging:
27
28```python3
29from typing import List
30
31
32class Solution:
33 def earliestTime(self, tasks: List[List[int]]) -> int:
34 # Track the earliest finish time seen so far.
35 earliest = float('inf')
36 for start_time, duration in tasks:
37 # Update the answer if this task finishes sooner.
38 earliest = min(earliest, start_time + duration)
39 return earliest
401class Solution {
2 public int earliestTime(int[][] tasks) {
3 // Initialize the answer with a sufficiently large value
4 // (acts as an upper bound based on the problem constraints)
5 int earliestFinishTime = 200;
6
7 // Iterate over each task, where:
8 // task[0] = start time of the task
9 // task[1] = duration of the task
10 for (int[] task : tasks) {
11 int startTime = task[0];
12 int duration = task[1];
13
14 // The finish time of the current task is start time + duration;
15 // keep track of the minimum finish time seen so far
16 earliestFinishTime = Math.min(earliestFinishTime, startTime + duration);
17 }
18
19 // Return the earliest possible finish time among all tasks
20 return earliestFinishTime;
21 }
22}
23```
24
25**Explanation of the approach:**
26
271. **Problem intent**: Each task is described by a start time and a duration. The goal is to find the earliest moment at which any single task can be completed.
28
292. **Step-by-step reasoning**:
30 - For each task, its completion time is simply `start + duration`.
31 - Since tasks are independent, the earliest overall completion time is the minimum of these values.
32 - The initial value `200` serves as a safe upper bound given typical constraint limits (e.g., values ≤ 100 each, so the sum never exceeds 200).
33
343. **Complexity**:
35 - **Time**: O(n), a single pass over the task list.
36 - **Space**: O(1), only a single tracking variable is used.
37
38**Alternative perspective**: The same logic can be expressed more concisely with the Stream API:
39
40```java
41class Solution {
42 public int earliestTime(int[][] tasks) {
43 // Map each task to its finish time and take the minimum
44 return Arrays.stream(tasks)
45 .mapToInt(task -> task[0] + task[1])
46 .min()
47 .orElse(200);
48 }
49}
501class Solution {
2public:
3 int earliestTime(vector<vector<int>>& tasks) {
4 // Initialize the answer with a value larger than any possible finish time.
5 // Constraints guarantee start time + duration will not exceed this bound.
6 int earliestFinishTime = 200;
7
8 // Iterate over each task, where:
9 // task[0] = start time of the task
10 // task[1] = duration required to complete the task
11 for (const auto& task : tasks) {
12 int startTime = task[0];
13 int duration = task[1];
14
15 // The finish time of the current task is start time plus duration.
16 // Keep track of the minimum finish time seen so far.
17 earliestFinishTime = min(earliestFinishTime, startTime + duration);
18 }
19
20 // Return the earliest time at which any single task can be completed.
21 return earliestFinishTime;
22 }
23};
241/**
2 * Finds the earliest time at which any task can be completed.
3 *
4 * Each task is represented as a pair [startTime, duration],
5 * so its completion time is startTime + duration.
6 * The answer is the minimum completion time among all tasks.
7 *
8 * @param tasks - A list of tasks, where each task is [startTime, duration]
9 * @returns The earliest completion time among all tasks
10 */
11function earliestTime(tasks: number[][]): number {
12 // Track the smallest completion time found so far,
13 // initialized to positive infinity so any task will be smaller
14 let earliestFinish: number = Number.POSITIVE_INFINITY;
15
16 // Iterate over every task to compute its completion time
17 for (const task of tasks) {
18 const startTime: number = task[0]; // Time when the task starts
19 const duration: number = task[1]; // How long the task takes
20
21 // Completion time of the current task
22 const finishTime: number = startTime + duration;
23
24 // Update the minimum if this task finishes earlier
25 earliestFinish = Math.min(earliestFinish, finishTime);
26 }
27
28 return earliestFinish;
29}
30```
31
32A few notes on the rewrite:
33
341. **Explicit loop instead of spread**: The original `Math.min(...tasks.map(...))` is concise, but spreading a very large array into `Math.min` can hit the call-stack argument limit. The loop version is safe for any input size and avoids creating an intermediate array.
35
362. **Naming**: The method name `earliestTime` is kept unchanged as required; internal variables use descriptive camelCase names (`earliestFinish`, `startTime`, `duration`, `finishTime`).
37
383. **Alternative (functional) version** — if you prefer the original one-liner style with clearer naming:
39
40```typescript
41/**
42 * Finds the earliest completion time (startTime + duration) among all tasks.
43 *
44 * @param tasks - A list of tasks, where each task is [startTime, duration]
45 * @returns The earliest completion time among all tasks
46 */
47function earliestTime(tasks: number[][]): number {
48 // Use reduce to fold the tasks into the minimum completion time,
49 // which avoids the argument-spreading limitation of Math.min(...)
50 return tasks.reduce(
51 (minFinish: number, [startTime, duration]: number[]) =>
52 Math.min(minFinish, startTime + duration),
53 Number.POSITIVE_INFINITY
54 );
55}
56Time and Space Complexity
-
Time Complexity:
O(n), wherenis the length of thetasksarray. The code iterates through every task[s, t]exactly once via the generator expressions + t for s, t in tasks, and theminfunction processes each computed value in a single pass. -
Space Complexity:
O(1). The generator expression evaluates values lazily one at a time without materializing an intermediate list, so only a constant amount of extra space is used regardless of input size.
Common Pitfalls
Pitfall 1: Minimizing start time or duration independently instead of the sum
A frequent mistake is assuming the task that starts earliest or the task with the shortest duration will also finish first, leading to code like:
# WRONG: combines the minimum start and minimum duration
# from two *different* tasks.
class Solution:
def earliestTime(self, tasks: List[List[int]]) -> int:
min_start = min(s for s, t in tasks)
min_duration = min(t for s, t in tasks)
return min_start + min_duration
or:
# WRONG: sorts by start time and assumes the first task finishes first.
class Solution:
def earliestTime(self, tasks: List[List[int]]) -> int:
tasks.sort()
return tasks[0][0] + tasks[0][1]
Why it fails: The earliest finish time depends on the combined value s + t of a single task, not on the minimum of each field taken separately. Consider tasks = [[1, 5], [3, 2]]:
- The wrong "min of each field" version returns
1 + 2 = 3, which corresponds to no real task. - The "sort by start" version returns
1 + 5 = 6, but task[3, 2]actually finishes at5.
Solution: Always compute the finish time s + t per task, then take the minimum over those sums:
class Solution:
def earliestTime(self, tasks: List[List[int]]) -> int:
return min(s + t for s, t in tasks)
Pitfall 2: Tracking the minimum with a poorly chosen initial value
In the explicit-loop variant, initializing the running minimum to 0 (or any small constant) silently produces wrong answers, since every valid finish time will be larger and never replace it:
# WRONG: earliest stays 0 forever.
earliest = 0
for s, t in tasks:
earliest = min(earliest, s + t)
return earliest
Solution: Initialize with float('inf'), or seed the answer with the first task's finish time:
earliest = float('inf')
for s, t in tasks:
earliest = min(earliest, s + t)
return earliest
Pitfall 3: Forgetting the empty-input edge case
If the problem constraints ever allow tasks to be empty, min(...) on an empty generator raises a ValueError. While most versions of this problem guarantee len(tasks) >= 1, defensive code (or interview settings) may require handling it:
class Solution:
def earliestTime(self, tasks: List[List[int]]) -> int:
if not tasks:
return -1 # or whatever sentinel the problem specifies
return min(s + t for s, t in tasks)
Takeaway: Confirm the constraints first — if tasks is guaranteed non-empty, the concise one-liner is safe; otherwise, guard against the empty case explicitly.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhich of these properties could exist for a graph but not a tree?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity describes how the time needed
Want a Structured Path to Master System Design Too? Don’t Miss This!