2432. The Employee That Worked on the Longest Task
Problem Description
You have n
employees, each with a unique ID from 0
to n - 1
.
You're given a 2D array logs
where each entry logs[i] = [id_i, leaveTime_i]
represents:
id_i
: the ID of the employee who worked on thei
-th taskleaveTime_i
: the time when the employee finished thei
-th task (all leave times are unique)
The important timing rules are:
- The
0
-th task starts at time0
- Each subsequent task starts immediately after the previous one ends
- The
i
-th task starts right after the(i-1)
-th task ends
Your goal is to find the ID of the employee who worked on a single task for the longest duration. If multiple employees have the same longest working time, return the one with the smallest ID.
For example, if logs = [[0,3], [1,5], [0,9]]
:
- Employee 0 works on task 0 from time 0 to 3 (duration = 3)
- Employee 1 works on task 1 from time 3 to 5 (duration = 2)
- Employee 0 works on task 2 from time 5 to 9 (duration = 4)
In this case, employee 0 has the longest single task duration of 4, so we return 0.
The solution tracks the end time of the previous task and calculates each task's duration by subtracting the previous end time from the current leave time. It maintains the maximum duration seen and the corresponding employee ID, updating them when a longer duration is found or when the same duration is found with a smaller employee ID.
Intuition
The key insight is that we need to calculate the duration of each task, not just look at the leave times. Since tasks run consecutively without gaps, the duration of task i
is simply leaveTime[i] - leaveTime[i-1]
(with task 0 starting at time 0).
Think of it like a relay race where each runner starts exactly when the previous one finishes. To find who ran the longest, we need to measure each person's individual segment, not their finish time.
We can solve this in a single pass through the logs by:
- Keeping track of when the last task ended (initially 0)
- For each log entry, calculating the task duration as
current_leave_time - last_end_time
- Comparing this duration with our current maximum
The tricky part is handling ties - when two employees have the same maximum duration, we need the one with the smaller ID. This means when we find a duration equal to our current maximum, we should only update our answer if the new employee's ID is smaller.
By maintaining just three variables (last
for the previous end time, mx
for the maximum duration seen, and ans
for the corresponding employee ID), we can efficiently track everything we need in one traversal. Each time we see a new task, we calculate its duration, check if it's a new maximum (or a tie with a better ID), and then update our last
time for the next iteration.
This greedy approach works because we only care about the single longest task duration - we don't need to track all durations or sort anything, just keep updating our best answer as we go.
Solution Approach
We implement a direct traversal approach using three variables to track our state:
last
: tracks the end time of the previous task (initialized to 0)mx
: stores the maximum task duration found so far (initialized to 0)ans
: stores the employee ID with the maximum duration (initialized to 0)
Let's walk through the implementation step by step:
-
Initialize tracking variables: Start with
last = mx = ans = 0
. The first task begins at time 0, solast
starts at 0. -
Iterate through each log entry: For each
[uid, t]
inlogs
:- Calculate the actual task duration:
t -= last
. This gives us the time spent on this specific task (current leave time minus previous task's end time).
- Calculate the actual task duration:
-
Update maximum if needed: Check if we found a better employee:
- If
mx < t
: This employee worked longer than any previous one, so update bothans = uid
andmx = t
- If
mx == t and ans > uid
: This employee worked the same duration as our current maximum but has a smaller ID, so updateans = uid
(keepingmx
the same)
- If
-
Update the last end time: Add the duration back to
last
withlast += t
. This prepares us for the next iteration where we'll need to know when the current task ended. -
Return the result: After processing all logs,
ans
contains the ID of the hardest worker.
The algorithm runs in O(m)
time where m
is the number of logs, with O(1)
space complexity since we only use three variables regardless of input size.
Example trace with logs = [[0,3], [1,5], [0,9]]
:
- Initially:
last=0, mx=0, ans=0
- Process
[0,3]
: duration =3-0=3
, updatemx=3, ans=0
,last=3
- Process
[1,5]
: duration =5-3=2
, no update (2 < 3),last=5
- Process
[0,9]
: duration =9-5=4
, updatemx=4, ans=0
,last=9
- Return
0
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with logs = [[2,6], [0,8], [2,12], [1,15]]
.
We'll track three variables:
last
: end time of previous task (starts at 0)mx
: maximum duration seen so far (starts at 0)ans
: employee ID with maximum duration (starts at 0)
Step 1: Process [2,6]
- Task duration:
6 - 0 = 6
(task runs from time 0 to 6) - Since
6 > mx(0)
, update:mx = 6
,ans = 2
- Update
last = 6
for next iteration - State:
last=6, mx=6, ans=2
Step 2: Process [0,8]
- Task duration:
8 - 6 = 2
(task runs from time 6 to 8) - Since
2 < mx(6)
, no updates tomx
orans
- Update
last = 8
- State:
last=8, mx=6, ans=2
Step 3: Process [2,12]
- Task duration:
12 - 8 = 4
(task runs from time 8 to 12) - Since
4 < mx(6)
, no updates tomx
orans
- Update
last = 12
- State:
last=12, mx=6, ans=2
Step 4: Process [1,15]
- Task duration:
15 - 12 = 3
(task runs from time 12 to 15) - Since
3 < mx(6)
, no updates tomx
orans
- Update
last = 15
- Final state:
last=15, mx=6, ans=2
Result: Employee 2 worked the longest single task (6 time units), so we return 2
.
The key insight is that we're comparing individual task durations, not total work time. Employee 2 appears twice in the logs but we only care about their longest single task, which was their first one with duration 6.
Solution Implementation
1class Solution:
2 def hardestWorker(self, n: int, logs: List[List[int]]) -> int:
3 """
4 Find the worker who worked the longest on a single task.
5
6 Args:
7 n: Total number of workers (IDs from 0 to n-1)
8 logs: List of [worker_id, end_time] pairs in chronological order
9
10 Returns:
11 The ID of the hardest worker (smallest ID in case of tie)
12 """
13 # Initialize variables
14 last_end_time = 0 # Track the end time of the previous task
15 max_duration = 0 # Maximum duration worked on a single task
16 hardest_worker_id = 0 # ID of the worker with maximum duration
17
18 # Process each log entry
19 for worker_id, end_time in logs:
20 # Calculate duration of current task (difference from last end time)
21 current_duration = end_time - last_end_time
22
23 # Update hardest worker if:
24 # 1. Current duration is strictly greater than max, OR
25 # 2. Current duration equals max AND current worker ID is smaller
26 if max_duration < current_duration or (max_duration == current_duration and hardest_worker_id > worker_id):
27 hardest_worker_id = worker_id
28 max_duration = current_duration
29
30 # Update last end time for next iteration
31 last_end_time = end_time
32
33 return hardest_worker_id
34
1class Solution {
2 public int hardestWorker(int n, int[][] logs) {
3 // Track the ID of the hardest worker
4 int hardestWorkerId = 0;
5
6 // Track the end time of the previous task and maximum work duration
7 int previousEndTime = 0;
8 int maxWorkDuration = 0;
9
10 // Iterate through each log entry
11 for (int[] log : logs) {
12 int workerId = log[0];
13 int currentEndTime = log[1];
14
15 // Calculate the duration of the current task
16 int currentTaskDuration = currentEndTime - previousEndTime;
17
18 // Update hardest worker if:
19 // 1. Current task duration is greater than max, OR
20 // 2. Current task duration equals max AND current worker ID is smaller
21 if (maxWorkDuration < currentTaskDuration ||
22 (maxWorkDuration == currentTaskDuration && hardestWorkerId > workerId)) {
23 hardestWorkerId = workerId;
24 maxWorkDuration = currentTaskDuration;
25 }
26
27 // Update the previous end time for the next iteration
28 previousEndTime = currentEndTime;
29 }
30
31 return hardestWorkerId;
32 }
33}
34
1class Solution {
2public:
3 int hardestWorker(int n, vector<vector<int>>& logs) {
4 // Track the employee ID with the longest task duration
5 int hardestEmployeeId = 0;
6 // Track the maximum task duration found so far
7 int maxTaskDuration = 0;
8 // Track the end time of the previous task
9 int previousEndTime = 0;
10
11 // Iterate through each log entry
12 for (auto& log : logs) {
13 int employeeId = log[0];
14 int currentEndTime = log[1];
15
16 // Calculate the duration of the current task
17 int taskDuration = currentEndTime - previousEndTime;
18
19 // Update the hardest worker if:
20 // 1. Current task duration is longer than the max, OR
21 // 2. Current task duration equals the max AND current employee ID is smaller
22 if (maxTaskDuration < taskDuration ||
23 (maxTaskDuration == taskDuration && hardestEmployeeId > employeeId)) {
24 maxTaskDuration = taskDuration;
25 hardestEmployeeId = employeeId;
26 }
27
28 // Update the previous end time for the next iteration
29 previousEndTime = currentEndTime;
30 }
31
32 return hardestEmployeeId;
33 }
34};
35
1/**
2 * Finds the ID of the hardest working employee based on work session logs.
3 * The hardest worker is determined by the longest single work session.
4 * In case of a tie, returns the employee with the smallest ID.
5 *
6 * @param n - The number of employees (employee IDs range from 0 to n-1)
7 * @param logs - Array of [employeeId, leaveTime] pairs representing work sessions
8 * @returns The ID of the hardest working employee
9 */
10function hardestWorker(n: number, logs: number[][]): number {
11 // Initialize variables to track the result
12 let hardestWorkerId: number = 0; // ID of the current hardest worker
13 let maxWorkDuration: number = 0; // Maximum work duration found so far
14 let previousLeaveTime: number = 0; // End time of the previous work session
15
16 // Iterate through each log entry
17 for (const [employeeId, leaveTime] of logs) {
18 // Calculate the duration of current work session
19 const currentWorkDuration: number = leaveTime - previousLeaveTime;
20
21 // Update hardest worker if current session is longer,
22 // or if it's equal but employee ID is smaller
23 if (maxWorkDuration < currentWorkDuration ||
24 (maxWorkDuration === currentWorkDuration && hardestWorkerId > employeeId)) {
25 hardestWorkerId = employeeId;
26 maxWorkDuration = currentWorkDuration;
27 }
28
29 // Update the previous leave time for next iteration
30 previousLeaveTime = leaveTime;
31 }
32
33 return hardestWorkerId;
34}
35
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array logs
. The algorithm iterates through the logs array exactly once, performing constant-time operations (comparisons, assignments, and arithmetic operations) for each element.
The space complexity is O(1)
. The algorithm only uses a fixed number of variables (last
, mx
, ans
) to track the state regardless of the input size, requiring constant extra space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Handling Tie-Breaking Logic
The Pitfall: A common mistake is using <=
instead of <
when comparing durations for the tie-breaking condition. This would incorrectly update the answer even when a later employee with a larger ID has the same duration.
# WRONG - This always updates when durations are equal, even for larger IDs if max_duration <= current_duration: hardest_worker_id = worker_id max_duration = current_duration
The Solution: Use the correct compound condition that explicitly checks both cases:
# CORRECT - Only update if duration is greater OR (equal duration AND smaller ID) if max_duration < current_duration or (max_duration == current_duration and hardest_worker_id > worker_id): hardest_worker_id = worker_id max_duration = current_duration
2. Misunderstanding Task Start Times
The Pitfall: Assuming each task starts at the previous task's leaveTime
instead of understanding that tasks are consecutive. Some might try to track individual start times or create unnecessary complexity.
# WRONG - Overcomplicating by trying to track start times separately
start_times = [0]
for i in range(1, len(logs)):
start_times.append(logs[i-1][1])
The Solution: Simply track the previous end time and calculate duration as the difference:
# CORRECT - Simple tracking of previous end time last_end_time = 0 for worker_id, end_time in logs: current_duration = end_time - last_end_time # ... rest of logic last_end_time = end_time
3. Forgetting to Initialize the Answer Variable
The Pitfall: Not initializing hardest_worker_id
or initializing it to an invalid value like -1
or n
. This causes issues when all tasks have duration 0 or when the first task needs to be the answer.
# WRONG - Invalid initialization hardest_worker_id = -1 # or hardest_worker_id = n
The Solution: Initialize to a valid worker ID (typically 0):
# CORRECT - Valid initialization hardest_worker_id = 0
4. Modifying Input Data Instead of Using Local Variables
The Pitfall: Directly modifying the end_time
in the logs array, which could cause issues if the array needs to be reused or in languages where modifications affect the original data.
# WRONG - Modifying input data
for i in range(len(logs)):
logs[i][1] -= last_end_time # Modifying original data
# ... rest of logic
The Solution: Use local variables to store calculated values:
# CORRECT - Using local variable for worker_id, end_time in logs: current_duration = end_time - last_end_time # Local calculation # ... rest of logic
What's the output of running the following function using input 56
?
1KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13 def dfs(path, res):
14 if len(path) == len(digits):
15 res.append(''.join(path))
16 return
17
18 next_number = digits[len(path)]
19 for letter in KEYBOARD[next_number]:
20 path.append(letter)
21 dfs(path, res)
22 path.pop()
23
24 res = []
25 dfs([], res)
26 return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2 '2', "abc".toCharArray(),
3 '3', "def".toCharArray(),
4 '4', "ghi".toCharArray(),
5 '5', "jkl".toCharArray(),
6 '6', "mno".toCharArray(),
7 '7', "pqrs".toCharArray(),
8 '8', "tuv".toCharArray(),
9 '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13 List<String> res = new ArrayList<>();
14 dfs(new StringBuilder(), res, digits.toCharArray());
15 return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19 if (path.length() == digits.length) {
20 res.add(path.toString());
21 return;
22 }
23 char next_digit = digits[path.length()];
24 for (char letter : KEYBOARD.get(next_digit)) {
25 path.append(letter);
26 dfs(path, res, digits);
27 path.deleteCharAt(path.length() - 1);
28 }
29}
30
1const KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13 let res = [];
14 dfs(digits, [], res);
15 return res;
16}
17
18function dfs(digits, path, res) {
19 if (path.length === digits.length) {
20 res.push(path.join(''));
21 return;
22 }
23 let next_number = digits.charAt(path.length);
24 for (let letter of KEYBOARD[next_number]) {
25 path.push(letter);
26 dfs(digits, path, res);
27 path.pop();
28 }
29}
30
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 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
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 represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!