2073. Time Needed to Buy Tickets
Problem Description
You have n
people standing in a line waiting to buy tickets. Each person is numbered from 0
to n-1
, where person 0
is at the front of the line and person n-1
is at the back.
You're given an array tickets
where tickets[i]
represents how many tickets the i
th person wants to buy.
The ticket buying process works as follows:
- Each person takes exactly 1 second to buy 1 ticket
- A person can only buy 1 ticket at a time
- After buying a ticket, if they need more tickets, they must go to the back of the line instantly
- When a person has bought all their tickets, they leave the line
Your task is to find how many seconds it takes for the person at position k
to finish buying all their tickets.
Example walkthrough:
If tickets = [2, 3, 2]
and k = 2
:
- Initially: Person 0 needs 2 tickets, Person 1 needs 3 tickets, Person 2 needs 2 tickets
- Second 1: Person 0 buys 1 ticket, goes to back (now needs 1 more)
- Second 2: Person 1 buys 1 ticket, goes to back (now needs 2 more)
- Second 3: Person 2 buys 1 ticket, goes to back (now needs 1 more)
- Second 4: Person 0 buys their last ticket and leaves
- Second 5: Person 1 buys 1 ticket, goes to back (now needs 1 more)
- Second 6: Person 2 buys their last ticket and leaves
Person 2 finishes at second 6, so the answer is 6.
The key insight is that when person k
finishes buying tickets:
- People before position
k
in the original line will buy at mosttickets[k]
tickets - People after position
k
in the original line will buy at mosttickets[k] - 1
tickets (since they get fewer chances before personk
finishes)
Intuition
Instead of simulating the entire queue process, let's think about what happens to each person when person k
finishes buying their tickets.
The key observation is that we don't need to track the queue's state at each second. We just need to count how many tickets each person will have bought by the time person k
finishes.
Let's think about two scenarios:
-
People originally in front of or at position
k
(positions0
tok
): These people get to buy tickets before personk
in every round of the queue. So if personk
needstickets[k]
tickets, these people will have the opportunity to buy up totickets[k]
tickets. However, if someone needs fewer tickets thantickets[k]
, they'll leave the line early after buying all they need. Therefore, each person at positioni
wherei <= k
contributesmin(tickets[i], tickets[k])
seconds. -
People originally behind position
k
(positionsk+1
ton-1
): These people start behind personk
, so in the final round when personk
buys their last ticket, these people won't get another chance. They get one less opportunity compared to personk
. Therefore, each person at positioni
wherei > k
contributesmin(tickets[i], tickets[k] - 1)
seconds.
The total time is simply the sum of all these individual contributions. Each ticket bought by anyone before person k
finishes adds 1 second to our answer.
This transforms the problem from a complex queue simulation into a simple calculation: for each person, determine how many tickets they'll buy before person k
finishes, and sum these amounts.
Learn more about Queue patterns.
Solution Approach
The implementation follows directly from our intuition using a single pass through the array:
-
Initialize a counter: Start with
ans = 0
to accumulate the total time. -
Iterate through each person: Use
enumerate()
to get both the indexi
and the number of ticketsx
that personi
wants. -
Calculate contribution for each person:
- If
i <= k
(person is at or before positionk
): They can buy up totickets[k]
tickets, but limited by their own need. So we addmin(x, tickets[k])
to the answer. - If
i > k
(person is after positionk
): They get one less opportunity, so we addmin(x, tickets[k] - 1)
to the answer.
- If
-
Return the total: The accumulated sum represents the total seconds needed.
The solution can be written concisely using a ternary operator:
ans += min(x, tickets[k] if i <= k else tickets[k] - 1)
This expression elegantly handles both cases:
- When
i <= k
, we compare withtickets[k]
- When
i > k
, we compare withtickets[k] - 1
Time Complexity: O(n)
where n
is the number of people, as we make a single pass through the array.
Space Complexity: O(1)
as we only use a constant amount of extra space for the counter variable.
The beauty of this solution is that it avoids simulating the queue operations entirely, reducing what could be an O(n * max(tickets))
simulation to a simple O(n)
calculation.
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 concrete example with tickets = [5, 1, 1, 1]
and k = 0
.
We want to find how long it takes for person 0 (who needs 5 tickets) to finish buying all their tickets.
Using our solution approach:
Instead of simulating the queue, we calculate how many tickets each person buys before person 0 finishes:
-
Person 0 (i = 0, needs 5 tickets):
- Since
i = 0 <= k = 0
, this person is at or before position k - They will buy
min(5, tickets[0]) = min(5, 5) = 5
tickets - Contribution: 5 seconds
- Since
-
Person 1 (i = 1, needs 1 ticket):
- Since
i = 1 > k = 0
, this person is after position k - They get one less opportunity than person 0
- They will buy
min(1, tickets[0] - 1) = min(1, 4) = 1
ticket - Contribution: 1 second
- Since
-
Person 2 (i = 2, needs 1 ticket):
- Since
i = 2 > k = 0
, this person is after position k - They will buy
min(1, tickets[0] - 1) = min(1, 4) = 1
ticket - Contribution: 1 second
- Since
-
Person 3 (i = 3, needs 1 ticket):
- Since
i = 3 > k = 0
, this person is after position k - They will buy
min(1, tickets[0] - 1) = min(1, 4) = 1
ticket - Contribution: 1 second
- Since
Total time = 5 + 1 + 1 + 1 = 8 seconds
This makes sense because:
- Person 0 buys 5 tickets total (taking 5 seconds for their purchases)
- Each of the 3 people behind person 0 gets to buy 1 ticket before person 0 completes (adding 3 more seconds)
- The people behind person 0 can't buy more than 4 tickets each (tickets[0] - 1), but they only need 1 ticket anyway
The key insight is that we don't need to track the queue state at each second - we just count how many tickets everyone buys before person k finishes.
Solution Implementation
1class Solution:
2 def timeRequiredToBuy(self, tickets: List[int], k: int) -> int:
3 """
4 Calculate the time required for person at position k to buy all their tickets.
5
6 Each person takes 1 second to buy 1 ticket, and they go to the back of the line
7 after buying 1 ticket. The process continues until person k buys all their tickets.
8
9 Args:
10 tickets: List where tickets[i] is the number of tickets person i wants to buy
11 k: The index of the person we're tracking
12
13 Returns:
14 Total time in seconds for person k to buy all their tickets
15 """
16 total_time = 0
17
18 # Iterate through each person in the queue
19 for index, ticket_count in enumerate(tickets):
20 # For people before or at position k:
21 # They can buy at most tickets[k] tickets before person k finishes
22 if index <= k:
23 total_time += min(ticket_count, tickets[k])
24 # For people after position k:
25 # They can buy at most tickets[k] - 1 tickets before person k finishes
26 # (because person k will leave after buying their last ticket)
27 else:
28 total_time += min(ticket_count, tickets[k] - 1)
29
30 return total_time
31
1class Solution {
2 public int timeRequiredToBuy(int[] tickets, int k) {
3 int totalTime = 0;
4
5 // Iterate through each person in the queue
6 for (int i = 0; i < tickets.length; i++) {
7 // For people at or before position k:
8 // They can buy at most tickets[k] tickets (same as person k)
9 // For people after position k:
10 // They can buy at most tickets[k] - 1 tickets
11 // (since person k leaves after buying their last ticket)
12
13 if (i <= k) {
14 // People before or at position k can buy up to tickets[k] tickets
15 totalTime += Math.min(tickets[i], tickets[k]);
16 } else {
17 // People after position k can buy up to tickets[k] - 1 tickets
18 totalTime += Math.min(tickets[i], tickets[k] - 1);
19 }
20 }
21
22 return totalTime;
23 }
24}
25
1class Solution {
2public:
3 int timeRequiredToBuy(vector<int>& tickets, int k) {
4 int totalTime = 0;
5
6 // Iterate through each person in the queue
7 for (int i = 0; i < tickets.size(); ++i) {
8 // For people at or before position k:
9 // They can buy at most tickets[k] tickets (same as person k)
10 // For people after position k:
11 // They can buy at most tickets[k] - 1 tickets
12 // (since person k leaves after buying their last ticket)
13 if (i <= k) {
14 // People before or at position k contribute min(their tickets, tickets[k])
15 totalTime += min(tickets[i], tickets[k]);
16 } else {
17 // People after position k contribute min(their tickets, tickets[k] - 1)
18 totalTime += min(tickets[i], tickets[k] - 1);
19 }
20 }
21
22 return totalTime;
23 }
24};
25
1/**
2 * Calculates the time required for person at position k to buy all their tickets
3 * @param tickets - Array where tickets[i] represents number of tickets person i wants to buy
4 * @param k - Index of the person we're tracking
5 * @returns Total time units required for person k to finish buying tickets
6 */
7function timeRequiredToBuy(tickets: number[], k: number): number {
8 // Initialize total time counter
9 let totalTime: number = 0;
10
11 // Get the length of the tickets array
12 const peopleCount: number = tickets.length;
13
14 // Iterate through each person in the queue
15 for (let currentIndex: number = 0; currentIndex < peopleCount; currentIndex++) {
16 // Calculate time contribution from current person
17 // If current person is before or at position k: they can buy up to tickets[k] tickets
18 // If current person is after position k: they can buy up to tickets[k] - 1 tickets
19 // (since person k leaves after buying their last ticket)
20 const maxTicketsToBuy: number = currentIndex <= k ? tickets[k] : tickets[k] - 1;
21
22 // Add the minimum between what current person wants and the maximum they can buy
23 totalTime += Math.min(tickets[currentIndex], maxTicketsToBuy);
24 }
25
26 return totalTime;
27}
28
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the tickets array. The algorithm iterates through the tickets array exactly once using a single for loop with enumerate. Within each iteration, it performs constant time operations: accessing array elements, comparison with min(), and addition. Therefore, the overall time complexity is linear with respect to the input size.
The space complexity is O(1)
. The algorithm only uses a fixed amount of extra space regardless of the input size. It maintains only two variables: ans
for accumulating the result and the loop variables i
and x
from enumerate. No additional data structures that scale with the input are created, resulting in constant space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Off-by-One Error for People After Position k
The Mistake:
A common error is treating all people the same way, forgetting that people after position k
get one fewer opportunity to buy tickets before person k
finishes.
# INCORRECT - treats everyone the same
for i, x in enumerate(tickets):
total_time += min(x, tickets[k]) # Wrong for i > k!
Why It Happens:
It's intuitive to think everyone gets the same number of chances, but in the circular queue, person k
exits immediately after buying their last ticket, giving people behind them one less turn.
The Fix:
Always distinguish between people before/at position k
and those after:
if index <= k:
total_time += min(ticket_count, tickets[k])
else:
total_time += min(ticket_count, tickets[k] - 1)
Pitfall 2: Simulating the Queue Instead of Calculating Directly
The Mistake: Implementing an actual queue simulation with dequeue and enqueue operations:
# INEFFICIENT - simulating the actual queue
from collections import deque
queue = deque([(i, tickets[i]) for i in range(len(tickets))])
time = 0
while queue:
person_id, remaining = queue.popleft()
time += 1
if remaining > 1:
queue.append((person_id, remaining - 1))
if person_id == k and remaining == 1:
return time
Why It Happens: The problem description naturally leads to thinking about simulating the actual queue process step-by-step.
The Fix:
Recognize that you can calculate the total time mathematically without simulation. The key insight is understanding how many tickets each person can buy before person k
finishes.
Pitfall 3: Incorrect Boundary Check
The Mistake:
Using strict inequality instead of inclusive comparison for position k
:
# INCORRECT - wrong boundary
if i < k: # Should be i <= k
total_time += min(ticket_count, tickets[k])
Why It Happens:
Confusion about whether person k
themselves should be included in the "before or at" category.
The Fix:
Remember that person k
needs to buy all tickets[k]
tickets, so they should be in the first category with i <= k
.
Pitfall 4: Edge Case with tickets[k] = 1
The Mistake:
Not handling the case where tickets[k] - 1 = 0
for people after position k
:
# Potential issue if not careful
if i > k:
total_time += min(ticket_count, tickets[k] - 1) # What if tickets[k] = 1?
Why It Happens:
When tickets[k] = 1
, people after position k
get 0 chances (since tickets[k] - 1 = 0
).
The Fix:
The min()
function naturally handles this case correctly. When tickets[k] - 1 = 0
, min(ticket_count, 0) = 0
, which correctly adds 0 to the total time. No special handling needed, but be aware of this edge case when testing.
The three-steps of Depth First Search are:
- Identify states;
- Draw the state-space tree;
- DFS on the state-space tree.
Recommended Readings
Queue Intro Think of the last time you stood in line to buy movie tickets The first person in line gets their ticket first and any newcomers join the end of the line This system where the first person to arrive is the first to be served is a queue in real
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
Want a Structured Path to Master System Design Too? Don’t Miss This!