Facebook Pixel

3386. Button with Longest Push Time

Problem Description

You are given a 2D array events that represents a sequence of button presses on a keyboard by a child.

Each element events[i] = [index_i, time_i] indicates:

  • index_i: the index/identifier of the button that was pressed
  • time_i: the time (in some unit) when this button was pressed

The array has the following properties:

  • It is sorted in increasing order of time (so earlier events come first)
  • The time taken to press a button is calculated as the difference between its press time and the previous button's press time
  • For the first button in the sequence, the time taken is simply its timestamp (since there's no previous button)

Your task is to find which button took the longest time to press and return its index.

If multiple buttons are tied for the longest press time, return the button with the smallest index.

Example walkthrough:

If events = [[1, 2], [2, 5], [3, 9], [1, 15]], then:

  • Button 1 (first event): time taken = 2
  • Button 2 (second event): time taken = 5 - 2 = 3
  • Button 3 (third event): time taken = 9 - 5 = 4
  • Button 1 (fourth event): time taken = 15 - 9 = 6

Button 1 (from the fourth event) took the longest time (6 units), so we return 1.

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 track the time difference between consecutive button presses. Since the events are already sorted by time, we can process them sequentially.

For each button press (except the first one), the time taken is simply current_time - previous_time. The first button is a special case where the time taken equals its timestamp.

As we iterate through the events, we need to keep track of:

  1. The maximum time difference we've seen so far
  2. The index of the button that achieved this maximum time

The challenge is handling ties - when multiple buttons have the same press time, we need the one with the smallest index. This means when we encounter a button with the same time as our current maximum, we should only update our answer if the new button's index is smaller.

We can solve this in a single pass through the array by:

  • Starting with the first button as our initial answer (its time is simply its timestamp)
  • For each subsequent pair of consecutive events, calculating the time difference
  • Updating our answer whenever we find a longer time, or the same time with a smaller button index

The pairwise approach is elegant here because it naturally gives us consecutive pairs (previous_event, current_event), making it easy to calculate time differences while keeping track of which button index corresponds to each time difference.

Solution Approach

We implement a single-pass solution that examines consecutive pairs of events to find the button with the longest press time.

Initialization:

  • Set ans and t to the values from the first event: ans, t = events[0]
  • Here, ans stores the button index and t stores the time taken (which equals the timestamp for the first button)

Main Processing:

  • Use pairwise(events) to iterate through consecutive pairs of events
  • For each pair ((_, t1), (i, t2)):
    • t1 is the timestamp of the previous button press
    • t2 is the timestamp of the current button press
    • i is the index of the current button
    • We ignore the index of the previous button (using _) since we only care about the current button's index

Time Calculation:

  • For each pair, calculate the time difference: d = t2 - t1
  • This represents how long it took to press the current button

Update Logic:

  • Update our answer if either:
    1. d > t: The current button took strictly longer than our previous maximum
    2. d == t and i < ans: The current button took the same time but has a smaller index (breaking ties)
  • When updating, set both ans = i (new button index) and t = d (new maximum time)

Return Result:

  • After processing all pairs, return ans which holds the index of the button with the longest press time

The algorithm runs in O(n) time with O(1) extra space, making it optimal for this problem.

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 trace through a small example: events = [[2, 3], [1, 7], [2, 12]]

Step 1: Initialization

  • Start with the first event [2, 3]
  • ans = 2 (button index)
  • t = 3 (time taken for first button)

Step 2: Process second event [1, 7]

  • Previous event: [2, 3], Current event: [1, 7]
  • Calculate time difference: d = 7 - 3 = 4
  • Compare with current maximum:
    • Is d > t? → Is 4 > 3? → Yes!
    • Update: ans = 1, t = 4

Step 3: Process third event [2, 12]

  • Previous event: [1, 7], Current event: [2, 12]
  • Calculate time difference: d = 12 - 7 = 5
  • Compare with current maximum:
    • Is d > t? → Is 5 > 4? → Yes!
    • Update: ans = 2, t = 5

Result: Return ans = 2

Button 2 (from the third event) took the longest time of 5 units.

Tie-breaking example: If we had events = [[3, 2], [1, 5], [2, 8]]:

  • Button 3: time = 2
  • Button 1: time = 5 - 2 = 3
  • Button 2: time = 8 - 5 = 3

Buttons 1 and 2 both took 3 units. When processing button 2, we'd check: d == t and i < ans3 == 3 and 2 < 1? → No! So we keep ans = 1 (the smaller index).

Solution Implementation

1from typing import List
2from itertools import pairwise
3
4class Solution:
5    def buttonWithLongestTime(self, events: List[List[int]]) -> int:
6        # Initialize with the first button press
7        # The first button's time is its timestamp (time from start)
8        longest_button, longest_time = events[0]
9      
10        # Iterate through consecutive pairs of events
11        # Each pair represents (previous_event, current_event)
12        for (_, prev_timestamp), (button_index, curr_timestamp) in pairwise(events):
13            # Calculate the time taken to press the current button
14            time_difference = curr_timestamp - prev_timestamp
15          
16            # Update the answer if:
17            # 1. Current button took longer time, OR
18            # 2. Same time but current button has smaller index
19            if time_difference > longest_time or (time_difference == longest_time and button_index < longest_button):
20                longest_button = button_index
21                longest_time = time_difference
22      
23        return longest_button
24
1class Solution {
2    public int buttonWithLongestTime(int[][] events) {
3        // Initialize with the first button as having the longest press time
4        // The first button's press time is simply its timestamp (from time 0)
5        int buttonWithMaxTime = events[0][0];
6        int maxTimeDuration = events[0][1];
7      
8        // Iterate through remaining events starting from index 1
9        for (int eventIndex = 1; eventIndex < events.length; eventIndex++) {
10            // Extract current button index and timestamp
11            int currentButton = events[eventIndex][0];
12            int currentTimestamp = events[eventIndex][1];
13            int previousTimestamp = events[eventIndex - 1][1];
14          
15            // Calculate the duration between current and previous button press
16            int currentDuration = currentTimestamp - previousTimestamp;
17          
18            // Update the result if:
19            // 1. Current duration is strictly greater than max duration, OR
20            // 2. Current duration equals max duration AND current button index is smaller
21            if (currentDuration > maxTimeDuration || 
22                (currentDuration == maxTimeDuration && currentButton < buttonWithMaxTime)) {
23                buttonWithMaxTime = currentButton;
24                maxTimeDuration = currentDuration;
25            }
26        }
27      
28        return buttonWithMaxTime;
29    }
30}
31
1class Solution {
2public:
3    int buttonWithLongestTime(vector<vector<int>>& events) {
4        // Initialize with the first button and its time
5        // The first button's duration is simply its timestamp (from time 0)
6        int longestButton = events[0][0];
7        int longestDuration = events[0][1];
8      
9        // Iterate through remaining events starting from index 1
10        for (int i = 1; i < events.size(); ++i) {
11            // Extract current button index and timestamp
12            int currentButton = events[i][0];
13            int currentTime = events[i][1];
14            int previousTime = events[i - 1][1];
15          
16            // Calculate duration between current and previous button press
17            int duration = currentTime - previousTime;
18          
19            // Update the result if:
20            // 1. Current duration is longer than the longest found so far, OR
21            // 2. Duration is equal but current button index is smaller (tie-breaker)
22            if (duration > longestDuration || 
23                (duration == longestDuration && currentButton < longestButton)) {
24                longestButton = currentButton;
25                longestDuration = duration;
26            }
27        }
28      
29        return longestButton;
30    }
31};
32
1/**
2 * Finds the button with the longest press duration.
3 * If multiple buttons have the same longest duration, returns the one with the smallest index.
4 * 
5 * @param events - A 2D array where each element is [buttonIndex, timestamp]
6 * @returns The index of the button with the longest press duration
7 */
8function buttonWithLongestTime(events: number[][]): number {
9    // Initialize with the first button's data
10    // answerButton: the button index with longest duration so far
11    // maxDuration: the longest duration found so far
12    let answerButton: number = events[0][0];
13    let maxDuration: number = events[0][1];
14  
15    // Iterate through remaining events starting from index 1
16    for (let eventIndex = 1; eventIndex < events.length; ++eventIndex) {
17        // Extract current button index and timestamp
18        const currentButton: number = events[eventIndex][0];
19        const currentTimestamp: number = events[eventIndex][1];
20      
21        // Calculate duration since the previous event
22        const previousTimestamp: number = events[eventIndex - 1][1];
23        const currentDuration: number = currentTimestamp - previousTimestamp;
24      
25        // Update answer if current duration is longer, 
26        // or if it's equal but button index is smaller
27        if (currentDuration > maxDuration || 
28            (currentDuration === maxDuration && currentButton < answerButton)) {
29            answerButton = currentButton;
30            maxDuration = currentDuration;
31        }
32    }
33  
34    return answerButton;
35}
36

Time and Space Complexity

The time complexity is O(n), where n is the length of the array events. The algorithm uses pairwise to iterate through consecutive pairs of events, which means it traverses the array once. Each operation inside the loop (subtraction, comparison, and assignment) takes constant time O(1). Therefore, the overall time complexity is linear.

The space complexity is O(1). The algorithm only uses a fixed amount of extra space for variables ans, t, t1, t2, i, and d, regardless of the input size. The pairwise function returns an iterator that doesn't create additional space proportional to the input size, it just maintains pointers to consecutive elements.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Forgetting to Handle the First Button's Time Correctly

The Problem: A common mistake is to skip the first button entirely when calculating press times, only considering the time differences between consecutive pairs. This leads to incorrect results because the first button's press time (which equals its timestamp) might actually be the longest.

Incorrect Implementation:

def buttonWithLongestTime(self, events: List[List[int]]) -> int:
    longest_button = -1
    longest_time = 0
  
    # WRONG: Starting from index 1, missing the first button's time
    for i in range(1, len(events)):
        time_diff = events[i][1] - events[i-1][1]
        if time_diff > longest_time:
            longest_button = events[i][0]
            longest_time = time_diff
  
    return longest_button

Why It Fails: If events = [[5, 10], [2, 12], [3, 14]]:

  • Button 5: time = 10 (initial press)
  • Button 2: time = 12 - 10 = 2
  • Button 3: time = 14 - 12 = 2

The incorrect code would return button 2 or 3 (with time 2), but the correct answer is button 5 (with time 10).

The Solution: Always initialize your tracking variables with the first button's values before processing the pairs:

longest_button, longest_time = events[0]  # Correctly includes first button

Pitfall 2: Incorrect Tie-Breaking Logic

The Problem: When multiple buttons have the same longest press time, the problem requires returning the button with the smallest index. It's easy to misunderstand this requirement or implement it incorrectly.

Incorrect Implementation:

# WRONG: Using >= instead of > means we always take the last occurrence
if time_difference >= longest_time:  
    longest_button = button_index
    longest_time = time_difference

Why It Fails: If events = [[1, 5], [3, 10], [2, 15]]:

  • Button 1: time = 5
  • Button 3: time = 10 - 5 = 5
  • Button 2: time = 15 - 10 = 5

Three buttons are tied at 5 units. The incorrect code would return button 2 (last occurrence), but we should return button 1 (smallest index).

The Solution: Use explicit tie-breaking logic:

if time_difference > longest_time or (time_difference == longest_time and button_index < longest_button):
    longest_button = button_index
    longest_time = time_difference

This ensures we only update when we find a strictly longer time OR when times are equal but the current button has a smaller index.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?


Recommended Readings

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

Load More