Taro Logo

Find the Winner of the Circular Game

Medium
Google logo
Google
2 views
Topics:
ArraysRecursionDynamic Programming

There are n friends playing a game, seated in a circle numbered 1 to n clockwise. The game proceeds as follows:

  1. Start at friend 1.
  2. Count k friends clockwise (including the starting friend).
  3. The last friend counted leaves the circle.
  4. If more than one friend remains, repeat from the friend immediately clockwise of the one who left.
  5. The last friend wins.

Given n and k, return the winner.

Example 1:

n = 5, k = 2

Output: 3

Explanation:

  • Start at friend 1.
  • Count 2 friends: 1, 2. Friend 2 leaves.
  • Next start is friend 3.
  • Count 2 friends: 3, 4. Friend 4 leaves.
  • Next start is friend 5.
  • Count 2 friends: 5, 1. Friend 1 leaves.
  • Next start is friend 3.
  • Count 2 friends: 3, 5. Friend 5 leaves.
  • Friend 3 wins.

Example 2:

n = 6, k = 5

Output: 1

Friends leave in this order: 5, 4, 6, 2, 3. Friend 1 is the winner.

Can you implement a function to efficiently determine the winner given n and k?

Solution


Naive Approach: Simulation using a List

A straightforward approach is to simulate the game using a list (e.g., ArrayList in Java or a list in Python) to represent the circle of friends. We iterate through the list, removing elements based on the given k value until only one friend remains. This simulates the exact process described in the problem statement.

Algorithm:

  1. Initialize a list containing integers from 1 to n, representing the friends.
  2. Start with an index of 0 (representing the first friend).
  3. Iterate until only one friend remains in the list:
    • Calculate the index of the friend to be removed: (current_index + k - 1) % list.size().
    • Remove the friend at the calculated index.
    • Update the current index to the next friend: the new index is the same as the removed friend's index.
  4. Return the value of the last remaining friend in the list.

Code (Java):

import java.util.ArrayList;
import java.util.List;

class Solution {
    public int findTheWinner(int n, int k) {
        List<Integer> friends = new ArrayList<>();
        for (int i = 1; i <= n; i++) {
            friends.add(i);
        }

        int currentIndex = 0;
        while (friends.size() > 1) {
            currentIndex = (currentIndex + k - 1) % friends.size();
            friends.remove(currentIndex);
        }

        return friends.get(0);
    }
}

Time Complexity:

O(n*k) in the worst case. Because for n friends, we may have to traverse k positions in the list

Space Complexity:

O(n), because we store the initial n friends in a list.

Optimal Approach: Josephus Problem with Recursion or Iteration

The Josephus Problem is a classic mathematical puzzle that this problem is based on. It allows to solve the problem in O(n) time and O(1) space. The recursive formulation of the Josephus Problem is based on the following idea:

  • After the first friend is eliminated, we are left with a smaller circle of n-1 friends.
  • The problem reduces to finding the winner in this smaller circle, but with a shifted starting position.

The iterative approach exploits the property that the answer for n friends can be derived from the answer for n-1 friends.

Algorithm (Iterative):

  1. Initialize the winner to 0 (representing the friend before the first friend).
  2. Iterate from 1 to n:
    • Update the winner: winner = (winner + k) % i.
  3. Return winner + 1 (since the winner is 0-indexed).

Code (Java):

class Solution {
    public int findTheWinner(int n, int k) {
        int winner = 0;
        for (int i = 1; i <= n; i++) {
            winner = (winner + k) % i;
        }
        return winner + 1;
    }
}

Time Complexity:

O(n), as we iterate from 1 to n once.

Space Complexity:

O(1), as we only use a constant amount of extra space.

Edge Cases

  • n = 1: In this case, the first friend is trivially the winner. Both the naive and optimal solutions correctly handle this case.
  • k = 1: If k is 1, each friend in turn is eliminated. The naive solution simulates this directly. The optimal solution still functions correctly.
  • Large n and k: The optimal solution is significantly more efficient for large values of n and k due to its linear time complexity and constant space complexity.

Summary

The optimal approach based on the Josephus Problem dramatically improves the efficiency of finding the winner. By leveraging the mathematical properties of the problem, we can reduce both the time and space complexity, making it suitable for larger inputs.