3175. Find The First Player to win K Games in a Row
Problem Description
You have n
players numbered from 0
to n - 1
standing in a queue in order. Each player has a unique skill level given in the array skills
, where skills[i]
represents the skill level of player i
.
The competition works as follows:
- The first two players in the queue compete against each other
- The player with the higher skill level wins the game
- The winner stays at the front of the queue, while the loser moves to the back of the queue
- This process continues until one player wins
k
games in a row
Your task is to find the initial index of the player who first achieves k
consecutive wins.
For example, if skills = [4, 2, 6, 3, 9]
and k = 2
:
- Players 0 (skill 4) and 1 (skill 2) compete → Player 0 wins, Player 1 goes to the back
- Players 0 (skill 4) and 2 (skill 6) compete → Player 2 wins, Player 0 goes to the back, Player 2's win count = 1
- Players 2 (skill 6) and 3 (skill 3) compete → Player 2 wins, Player 3 goes to the back, Player 2's win count = 2
- Player 2 has won 2 consecutive games, so the answer is 2
The key insight is that we don't need to simulate the entire queue process. We can track the current winner and their consecutive win count while iterating through the array once. If a player defeats all other n-1
players, they are guaranteed to be the winner regardless of k
's value, which is why the solution uses k = min(k, n - 1)
.
Intuition
Let's think about what happens during the competition. Initially, players 0 and 1 compete. The winner stays at position 0, and we then compare this winner with player 2. Then with player 3, and so on.
The key observation is that we're essentially finding which player can beat k
consecutive opponents as we traverse through the array. The winner at any point stays at the front and faces the next player in line.
Here's the crucial insight: if we've gone through all n-1
other players (comparing them with our current champion), then our current champion has beaten everyone at least once. After this point, the champion will keep winning forever since they have the highest skill level. This means that if k >= n-1
, we just need to find the player with the maximum skill level.
That's why we can simplify the problem by setting k = min(k, n-1)
. We don't need to simulate beyond n-1
comparisons.
Now, instead of actually simulating the queue operations (which would be expensive), we can just iterate through the array once:
- Keep track of the current "champion" (initially player 0)
- Count their consecutive wins
- When we encounter a stronger player, they become the new champion with a win count of 1
- When the current champion beats another player, increment their win count
- Stop when someone reaches
k
wins
This linear scan approach works because the order of comparisons is exactly what would happen if we were actually moving losers to the back of the queue - each player gets compared with the current champion in their original order.
Solution Approach
The solution implements a single-pass algorithm that simulates the competition without actually manipulating a queue.
Key Steps:
-
Optimization of k: First, we set
k = min(k, n - 1)
. This is because after comparing with alln-1
other players, the current champion must be the strongest player overall and will win all future games. -
Initialize tracking variables:
i = 0
: Index of the current champion (starts with player 0)cnt = 0
: Count of consecutive wins for the current champion
-
Linear traversal: We iterate through players 1 to n-1 using index
j
:- Compare
skills[i]
(current champion) withskills[j]
(challenger) - If
skills[i] < skills[j]
:- Player
j
wins and becomes the new champion - Update
i = j
(new champion index) - Reset
cnt = 1
(new champion has 1 win)
- Player
- If
skills[i] >= skills[j]
:- Current champion wins
- Increment
cnt += 1
- Compare
-
Early termination: If at any point
cnt == k
, we've found our winner and can break out of the loop. -
Return the result: The index
i
represents the initial position of the winning player.
Why this works:
- The order of comparisons in our iteration matches exactly what would happen in the actual queue simulation
- Player at index
i
faces players at indicesi+1, i+2, ..., n-1
in sequence - If player
i
loses to playerj
, thenj
becomes the new champion and faces the remaining players - This avoids the O(n) space and time complexity of actual queue operations
Time Complexity: O(n) - single pass through the array Space Complexity: O(1) - only using a few variables to track state
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 skills = [3, 7, 2, 5, 1]
and k = 3
.
Step 1: Optimization
n = 5
, sok = min(3, 5-1) = min(3, 4) = 3
Step 2: Initialize
i = 0
(player 0 with skill 3 is our initial champion)cnt = 0
(no wins yet)
Step 3: Simulate competitions
Round 1 (j = 1):
- Compare player 0 (skill 3) vs player 1 (skill 7)
- 3 < 7, so player 1 wins
- Update:
i = 1
,cnt = 1
- Current champion: player 1 with 1 win
Round 2 (j = 2):
- Compare player 1 (skill 7) vs player 2 (skill 2)
- 7 > 2, so player 1 wins
- Update:
cnt = 2
- Current champion: player 1 with 2 wins
Round 3 (j = 3):
- Compare player 1 (skill 7) vs player 3 (skill 5)
- 7 > 5, so player 1 wins
- Update:
cnt = 3
- Current champion: player 1 with 3 wins
Step 4: Check termination
cnt == k
(3 == 3), so we found our winner!- Break the loop
Step 5: Return result
- Return
i = 1
Player at index 1 (with skill level 7) is the first to achieve 3 consecutive wins. Note how we didn't need to actually move players to the back of a queue - we just compared the current champion with each subsequent player in order, which gives us the same result as the actual queue simulation would.
Solution Implementation
1from typing import List
2
3
4class Solution:
5 def findWinningPlayer(self, skills: List[int], k: int) -> int:
6 """
7 Find the index of the player who wins k consecutive games.
8
9 In this game, players compete based on their skill values.
10 The player with higher skill wins and stays to face the next challenger.
11
12 Args:
13 skills: List of integers representing each player's skill level
14 k: Number of consecutive wins needed
15
16 Returns:
17 Index of the winning player
18 """
19 n = len(skills)
20
21 # Optimization: if k >= n-1, the strongest player will eventually win
22 # since they would face all other players
23 k = min(k, n - 1)
24
25 # Track the current champion's index and their consecutive win count
26 current_winner_index = 0
27 consecutive_wins = 0
28
29 # Iterate through all challengers starting from index 1
30 for challenger_index in range(1, n):
31 if skills[current_winner_index] < skills[challenger_index]:
32 # Challenger wins, becomes the new champion
33 current_winner_index = challenger_index
34 consecutive_wins = 1
35 else:
36 # Current champion wins, increment their win count
37 consecutive_wins += 1
38
39 # Check if current champion has reached k consecutive wins
40 if consecutive_wins == k:
41 break
42
43 return current_winner_index
44
1class Solution {
2 public int findWinningPlayer(int[] skills, int k) {
3 int arrayLength = skills.length;
4
5 // Optimization: if k >= n-1, the winner must have won against all other players
6 // So we cap k at n-1 since that's the maximum meaningful consecutive wins
7 int targetWins = Math.min(k, arrayLength - 1);
8
9 // Track the current champion (player who is currently winning)
10 int currentChampionIndex = 0;
11
12 // Track consecutive wins of the current champion
13 int consecutiveWins = 0;
14
15 // Iterate through all other players to challenge the champion
16 for (int challengerIndex = 1; challengerIndex < arrayLength; ++challengerIndex) {
17
18 // Check if challenger defeats the current champion
19 if (skills[currentChampionIndex] < skills[challengerIndex]) {
20 // Challenger wins and becomes the new champion
21 currentChampionIndex = challengerIndex;
22 consecutiveWins = 1; // Reset win count for new champion
23 } else {
24 // Current champion wins, increment their win count
25 ++consecutiveWins;
26 }
27
28 // Check if current champion has reached the target number of wins
29 if (consecutiveWins == targetWins) {
30 break; // Found the winner, exit early
31 }
32 }
33
34 // Return the index of the winning player
35 return currentChampionIndex;
36 }
37}
38
1class Solution {
2public:
3 int findWinningPlayer(vector<int>& skills, int k) {
4 int n = skills.size();
5
6 // Optimization: if k >= n-1, the winner must have won against all others
7 // So we cap k at n-1 to avoid unnecessary iterations
8 k = min(k, n - 1);
9
10 // Track the current champion (player at front of queue)
11 int currentChampion = 0;
12
13 // Count consecutive wins for the current champion
14 int consecutiveWins = 0;
15
16 // Simulate matches starting from position 1
17 for (int challenger = 1; challenger < n; ++challenger) {
18 if (skills[currentChampion] < skills[challenger]) {
19 // Current champion loses, challenger becomes new champion
20 currentChampion = challenger;
21 consecutiveWins = 1; // New champion has 1 win
22 } else {
23 // Current champion wins, increment win count
24 ++consecutiveWins;
25 }
26
27 // Check if current champion has reached k consecutive wins
28 if (consecutiveWins == k) {
29 break;
30 }
31 }
32
33 // Return the index of the winning player
34 return currentChampion;
35 }
36};
37
1/**
2 * Finds the index of the winning player in a game where players compete based on skills.
3 * Players compete in order, and a player wins if they win k consecutive rounds.
4 *
5 * @param skills - Array of skill values for each player
6 * @param k - Number of consecutive wins needed to be declared the winner
7 * @returns The index of the winning player
8 */
9function findWinningPlayer(skills: number[], k: number): number {
10 const playerCount: number = skills.length;
11
12 // Optimize k: if k >= playerCount - 1, the strongest player will definitely win
13 const winsNeeded: number = Math.min(k, playerCount - 1);
14
15 // Track current champion index and their consecutive win count
16 let currentChampionIndex: number = 0;
17 let consecutiveWins: number = 0;
18
19 // Simulate the competition starting from the second player
20 for (let challengerIndex: number = 1; challengerIndex < playerCount; challengerIndex++) {
21 if (skills[currentChampionIndex] < skills[challengerIndex]) {
22 // Challenger wins: becomes the new champion
23 currentChampionIndex = challengerIndex;
24 consecutiveWins = 1;
25 } else {
26 // Current champion wins: increment their win count
27 consecutiveWins++;
28 }
29
30 // Check if current champion has reached required wins
31 if (consecutiveWins === winsNeeded) {
32 break;
33 }
34 }
35
36 return currentChampionIndex;
37}
38
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the skills array.
The algorithm iterates through the skills array once with a single for loop from index 1 to n-1. In each iteration, it performs constant time operations (comparisons and variable updates). Even though there's a break statement that could terminate the loop early when cnt == k
, in the worst case scenario, the loop will run through all n-1
iterations (when the strongest player is at the beginning or when no player wins k
consecutive games before reaching the end).
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space. It maintains a few integer variables (n
, k
, i
, cnt
, j
) regardless of the input size. The modification of k
with min(k, n-1)
is also done in-place without creating any additional data structures. The input array is not modified and no auxiliary data structures are created.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Not Optimizing k Value
A common mistake is forgetting to cap k
at n-1
. Without this optimization, the algorithm might continue unnecessary iterations even after a player has defeated everyone else.
Incorrect approach:
# Without optimization
for challenger_index in range(1, n):
# ... competition logic ...
if consecutive_wins == k: # k could be very large
break
Why it's problematic: If k = 1000000
and n = 5
, after a player beats all 4 others, they're guaranteed to win all future matches. The loop would continue unnecessarily.
Solution: Always use k = min(k, n - 1)
at the beginning.
2. Incorrect Win Count Initialization
Some might initialize the consecutive wins counter incorrectly when a new champion emerges.
Incorrect approach:
if skills[current_winner_index] < skills[challenger_index]: current_winner_index = challenger_index consecutive_wins = 0 # Wrong! Should be 1
Why it's problematic: When a challenger defeats the current champion, they've just won their first game. Setting consecutive_wins = 0
would require an extra unnecessary iteration to reach k
wins.
Solution: Always set consecutive_wins = 1
when a new champion emerges, as they've just won their first game.
3. Off-by-One Error in Loop Range
Starting the loop from index 0 instead of 1, or misunderstanding the initial state.
Incorrect approach:
current_winner_index = 0
consecutive_wins = 1 # Wrong! Player 0 hasn't won yet
for challenger_index in range(0, n): # Wrong! Starts from 0
# ...
Why it's problematic:
- Player at index 0 starts as the first competitor but hasn't won any games yet
- Starting the loop from 0 would make player 0 compete against themselves
Solution: Initialize consecutive_wins = 0
and start the loop from index 1.
4. Mishandling the Case When k = 1
Special edge case where any single win should immediately return the winner.
Potential issue:
# If not handled properly, might miss immediate wins if k == 1: # First comparison determines the winner return 0 if skills[0] >= skills[1] else 1
Why it's problematic: While this specific handling works, it adds unnecessary complexity. The general algorithm already handles k = 1
correctly.
Solution: Trust the general algorithm to handle all cases uniformly, including k = 1
.
Which of the following array represent a max heap?
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!