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 pressedtime_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.
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:
- The maximum time difference we've seen so far
- 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
andt
to the values from the first event:ans, t = events[0]
- Here,
ans
stores the button index andt
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 presst2
is the timestamp of the current button pressi
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:
d > t
: The current button took strictly longer than our previous maximumd == 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) andt = 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 EvaluatorExample 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
? → Is4 > 3
? → Yes! - Update:
ans = 1
,t = 4
- Is
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
? → Is5 > 4
? → Yes! - Update:
ans = 2
,t = 5
- Is
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 < ans
→ 3 == 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.
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
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!