Facebook Pixel

1823. Find the Winner of the Circular Game

Problem Description

This problem is about simulating an elimination game known as the Josephus problem.

You have n friends sitting in a circle, numbered from 1 to n in clockwise order. They play an elimination game with the following rules:

  1. The game starts with friend number 1
  2. From the current position, count k friends in the clockwise direction (including the friend you start counting from)
  3. The k-th friend counted is eliminated from the circle
  4. If more than one friend remains, continue the game starting from the friend immediately clockwise to the eliminated friend
  5. Repeat until only one friend remains - this friend is the winner

The counting wraps around the circle, so after friend n, you continue with friend 1.

Example visualization:

  • If n = 5 and k = 2:
    • Start at friend 1, count 2 friends (1, 2) → friend 2 is eliminated
    • Start at friend 3, count 2 friends (3, 4) → friend 4 is eliminated
    • Start at friend 5, count 2 friends (5, 1) → friend 1 is eliminated
    • Start at friend 3, count 2 friends (3, 5) → friend 5 is eliminated
    • Friend 3 is the winner

The solution uses a recursive approach based on the mathematical property of the Josephus problem. The key insight is that if we know the winner's position for n-1 friends, we can calculate the winner's position for n friends using the formula: (k + winner(n-1, k)) % n. The base case is when n = 1, where the only friend (friend 1) is the winner.

The modulo operation handles the circular nature of the problem, and the special case when the result is 0 is adjusted to return n (since friends are numbered 1 to n, not 0 to n-1).

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

Intuition

The key insight comes from recognizing a pattern in how positions shift after each elimination.

Let's think about this problem step by step. When we have n friends and eliminate one, we're left with n-1 friends. The critical observation is that the winner among these n-1 friends will be the same as the original winner - just with a shifted position.

Consider what happens after the first elimination:

  • We start at position 1 and count k friends
  • Friend at position k is eliminated (using 1-based indexing)
  • Now we have n-1 friends, and we start counting from position k+1

Here's the breakthrough: If we solve the smaller problem with n-1 friends first, we can map that solution back to our original problem. The winner's position in the n-1 game corresponds to a specific position in the original n game.

Think of it this way:

  1. In the n-1 friends game, positions are numbered 1 to n-1
  2. But in the original game, after eliminating the k-th friend, the new "position 1" in the reduced game is actually position k+1 in the original game
  3. So if position p wins in the n-1 game, its actual position in the n game would be (k + p - 1) % n + 1

This leads to the recursive relationship: winner(n, k) = (k + winner(n-1, k)) % n

The modulo operation handles the circular wrapping. When the result is 0, we need to return n instead because we're using 1-based indexing (positions 1 to n) rather than 0-based.

The base case is straightforward: when there's only 1 friend (n = 1), that friend at position 1 is the winner.

This recursive approach elegantly reduces the problem size by 1 at each step, building up the solution from the simplest case to the full problem.

Learn more about Recursion, Queue and Math patterns.

Solution Approach

The solution implements a recursive approach to solve the Josephus problem efficiently.

Recursive Formula: The core of the solution relies on the mathematical relationship:

  • winner(n, k) = (k + winner(n-1, k)) % n

Implementation Details:

  1. Base Case: When n = 1, there's only one friend remaining, so friend 1 is the winner:

    if n == 1:
        return 1
  2. Recursive Case: For n > 1, we calculate the winner's position using the recursive formula:

    ans = (k + self.findTheWinner(n - 1, k)) % n

    This formula works because:

    • self.findTheWinner(n - 1, k) gives us the winner's position in a game with n-1 friends
    • Adding k adjusts for the shift in positions after the first elimination
    • The modulo n handles the circular nature of the problem
  3. Position Adjustment: Since we're using 1-based indexing (friends numbered 1 to n) but modulo gives us 0-based results:

    return n if ans == 0 else ans

    When ans == 0, it actually represents the n-th position in 1-based indexing.

Time and Space Complexity:

  • Time Complexity: O(n) - We make exactly n recursive calls, one for each value from n down to 1
  • Space Complexity: O(n) - The recursion stack depth is n

Example Walkthrough: For n = 5, k = 2:

  • findTheWinner(5, 2) calls findTheWinner(4, 2)
  • findTheWinner(4, 2) calls findTheWinner(3, 2)
  • findTheWinner(3, 2) calls findTheWinner(2, 2)
  • findTheWinner(2, 2) calls findTheWinner(1, 2)
  • findTheWinner(1, 2) returns 1 (base case)
  • Back-propagating:
    • findTheWinner(2, 2) = (2 + 1) % 2 = 1
    • findTheWinner(3, 2) = (2 + 1) % 3 = 0 → returns 3
    • findTheWinner(4, 2) = (2 + 3) % 4 = 1
    • findTheWinner(5, 2) = (2 + 1) % 5 = 3

The winner is friend 3.

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 n = 4 friends and k = 3 to illustrate the solution approach.

Initial Setup:

  • 4 friends sitting in a circle: [1, 2, 3, 4]
  • We count 3 positions each time to eliminate someone

Step-by-step Simulation (to understand the problem):

  1. Start at friend 1, count 3: [1, 2, 3] → Friend 3 is eliminated
    • Remaining: [1, 2, 4]
  2. Start at friend 4, count 3: [4, 1, 2] → Friend 2 is eliminated
    • Remaining: [1, 4]
  3. Start at friend 4, count 3: [4, 1, 4] → Friend 4 is eliminated
    • Remaining: [1]
  4. Friend 1 is the winner!

Now let's trace through our recursive solution:

The recursion builds from the base case upward:

findTheWinner(4, 3)
  ├── calls findTheWinner(3, 3)
      ├── calls findTheWinner(2, 3)
          ├── calls findTheWinner(1, 3)
              └── returns 1 (base case: only 1 friend)
          └── returns (3 + 1) % 2 = 0 → returns 2
      └── returns (3 + 2) % 3 = 2 → returns 2
  └── returns (3 + 2) % 4 = 1 → returns 1

Breaking down each recursive return:

  1. findTheWinner(1, 3) = 1

    • Base case: with 1 friend, friend 1 wins
  2. findTheWinner(2, 3) = (3 + 1) % 2 = 0 → returns 2

    • With 2 friends, after adding the shift and taking modulo
    • Since result is 0, we return n=2 (adjusting for 1-based indexing)
  3. findTheWinner(3, 3) = (3 + 2) % 3 = 2

    • With 3 friends, the winner is at position 2
  4. findTheWinner(4, 3) = (3 + 2) % 4 = 1

    • With 4 friends, the winner is at position 1

Why the formula works:

  • Each recursive call represents a game with one fewer friend
  • The formula (k + winner(n-1, k)) % n accounts for:
    • The position shift that occurs after each elimination (the k term)
    • The winner's position in the smaller game (the winner(n-1, k) term)
    • The circular nature of the problem (the % n operation)

The final answer is friend 1, which matches our manual simulation!

Solution Implementation

1class Solution:
2    def findTheWinner(self, n: int, k: int) -> int:
3        """
4        Find the winner of the Josephus game.
5      
6        Args:
7            n: Number of friends in the circle (1 to n)
8            k: Count for elimination (every k-th friend is eliminated)
9          
10        Returns:
11            The position of the winning friend (1-indexed)
12        """
13        # Base case: if only one person, they are the winner
14        if n == 1:
15            return 1
16      
17        # Recursive case: find winner for (n-1) people, then adjust position
18        # The formula uses 0-indexed positions internally:
19        # - findTheWinner(n-1, k) returns 1-indexed position for n-1 people
20        # - We add k to shift by the elimination step
21        # - Modulo n converts to 0-indexed position in current circle
22        winner_position = (k + self.findTheWinner(n - 1, k)) % n
23      
24        # Convert back to 1-indexed: if result is 0, it means position n
25        return n if winner_position == 0 else winner_position
26
1class Solution {
2    /**
3     * Finds the winner in the Josephus problem.
4     * In a circle of n friends numbered from 1 to n, every k-th friend is eliminated
5     * until only one remains.
6     * 
7     * @param n The total number of friends in the circle
8     * @param k The count for elimination (every k-th friend is eliminated)
9     * @return The position (1-indexed) of the winning friend
10     */
11    public int findTheWinner(int n, int k) {
12        // Base case: when there's only one person, they are the winner at position 1
13        if (n == 1) {
14            return 1;
15        }
16      
17        // Recursive case: find the winner for (n-1) friends and adjust the position
18        // The formula uses the Josephus problem recurrence relation:
19        // - Find winner position for (n-1) friends
20        // - Add k to shift the position after one elimination
21        // - Take modulo n to handle circular wrapping
22        int winnerPosition = (findTheWinner(n - 1, k) + k) % n;
23      
24        // Convert 0-indexed result back to 1-indexed
25        // If the result is 0, it means the last position (n-th position)
26        return winnerPosition == 0 ? n : winnerPosition;
27    }
28}
29
1class Solution {
2public:
3    int findTheWinner(int n, int k) {
4        // Base case: if only one person remains, they are the winner (1-indexed)
5        if (n == 1) {
6            return 1;
7        }
8      
9        // Recursive case: find the winner for (n-1) people
10        // Then adjust the position by adding k and taking modulo n
11        int winner_position = (findTheWinner(n - 1, k) + k) % n;
12      
13        // Since we're using 1-indexed positions, if the result is 0,
14        // it actually represents the nth position
15        return winner_position == 0 ? n : winner_position;
16    }
17};
18
1/**
2 * Finds the winner in the Josephus problem using recursion.
3 * In a circle of n friends, every k-th friend is eliminated until only one remains.
4 * 
5 * @param n - The number of friends in the circle
6 * @param k - The count for elimination (every k-th person is eliminated)
7 * @returns The position of the winning friend (1-indexed)
8 */
9function findTheWinner(n: number, k: number): number {
10    // Base case: when only one person remains, they are the winner at position 1
11    if (n === 1) {
12        return 1;
13    }
14  
15    // Recursive case: find the winner for (n-1) friends and adjust the position
16    // The formula (k + findTheWinner(n - 1, k)) % n converts the 0-indexed position
17    // from the smaller circle to the current circle
18    const winnerPosition: number = (k + findTheWinner(n - 1, k)) % n;
19  
20    // Convert from 0-indexed to 1-indexed position
21    // If the result is 0, it means the last position (n-th position)
22    return winnerPosition === 0 ? n : winnerPosition;
23}
24

Time and Space Complexity

Time Complexity: O(n)

The recursive function makes exactly n calls, starting from n down to the base case when n = 1. Each recursive call performs a constant amount of work (modulo operation and addition), so the total time complexity is O(n).

Space Complexity: O(n)

The space complexity is determined by the recursion call stack. Since the function recursively calls itself n times before reaching the base case, the maximum depth of the recursion stack is n. Therefore, the space complexity is O(n) due to the recursive calls.

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

Common Pitfalls

1. Index Confusion: 0-based vs 1-based Indexing

The most common pitfall in implementing the Josephus problem is mixing up 0-based and 1-based indexing. The mathematical formula works with 0-based indices internally, but the problem requires 1-based output (friends numbered 1 to n).

Incorrect Implementation:

def findTheWinner(self, n: int, k: int) -> int:
    if n == 1:
        return 1
    # Forgetting to handle the 0 case properly
    return (k + self.findTheWinner(n - 1, k)) % n

This would return 0 for certain cases, which is invalid since friends are numbered from 1 to n.

Correct Solution: Always check if the modulo result is 0 and convert it to n:

ans = (k + self.findTheWinner(n - 1, k)) % n
return n if ans == 0 else ans

2. Stack Overflow for Large n

The recursive solution has O(n) space complexity due to the call stack. For very large values of n (typically > 10^4), this could cause a stack overflow.

Iterative Solution to Avoid Stack Overflow:

def findTheWinner(self, n: int, k: int) -> int:
    # Start with 0-indexed position for 1 person
    winner = 0
  
    # Build up from 2 people to n people
    for i in range(2, n + 1):
        winner = (winner + k) % i
  
    # Convert from 0-indexed to 1-indexed
    return winner + 1

3. Incorrect Base Case Return Value

Some might incorrectly return 0 for the base case, thinking in 0-based terms:

Incorrect:

if n == 1:
    return 0  # Wrong! Friends are numbered 1 to n

Correct:

if n == 1:
    return 1  # The only friend is friend #1

4. Simulation Approach Performance Issues

While simulating the actual elimination process is intuitive, it's inefficient:

Inefficient Simulation Approach:

def findTheWinner(self, n: int, k: int) -> int:
    friends = list(range(1, n + 1))
    idx = 0
  
    while len(friends) > 1:
        idx = (idx + k - 1) % len(friends)
        friends.pop(idx)
  
    return friends[0]

This has O(n²) time complexity due to the list removal operations, compared to O(n) for the recursive/iterative mathematical solution.

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

Is the following code DFS or BFS?

void search(Node root) {
  if (!root) return;
  visit(root);
  root.visited = true;
  for (Node node in root.adjacent) {
    if (!node.visited) {
      search(node);
    }
  }
}

Recommended Readings

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

Load More