Facebook Pixel

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 the i-th task
  • leaveTime_i: the time when the employee finished the i-th task (all leave times are unique)

The important timing rules are:

  • The 0-th task starts at time 0
  • 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Keeping track of when the last task ended (initially 0)
  2. For each log entry, calculating the task duration as current_leave_time - last_end_time
  3. 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:

  1. Initialize tracking variables: Start with last = mx = ans = 0. The first task begins at time 0, so last starts at 0.

  2. Iterate through each log entry: For each [uid, t] in logs:

    • 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).
  3. Update maximum if needed: Check if we found a better employee:

    • If mx < t: This employee worked longer than any previous one, so update both ans = uid and mx = t
    • If mx == t and ans > uid: This employee worked the same duration as our current maximum but has a smaller ID, so update ans = uid (keeping mx the same)
  4. Update the last end time: Add the duration back to last with last += t. This prepares us for the next iteration where we'll need to know when the current task ended.

  5. 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, update mx=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, update mx=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 Evaluator

Example 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 to mx or ans
  • 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 to mx or ans
  • 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 to mx or ans
  • 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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More