There are n people standing in a circle, numbered from 0 to n - 1. Starting from person 0, count k people clockwise and remove the kth person. After the removal, begin counting again from the next person still in the circle. Repeat this process until only one person remains, and return that person's position.

Example:

Image represents a step-by-step process demonstrating a coding pattern, possibly related to data structure manipulation or algorithm execution.  The process begins with a set of five nodes (circles) labeled 0, 1, 2, 3, and 4, arranged somewhat arbitrarily.  Small numbers (1 and 2) are positioned near some nodes, potentially representing weights, priorities, or indices.  A node labeled '1' is crossed out with a gray 'X', indicating its removal or inactivation.  A rightward arrow indicates a transition to the next step.  In subsequent steps, the crossed-out node is removed, and other nodes (initially 3 and 2) become grayed out, suggesting they are also processed or removed in later stages.  The process continues with further graying out and removal of nodes, showing a sequence of transformations on the initial node arrangement.  The final state shows only nodes 0 and 2 remaining, with the other nodes grayed out and crossed out, implying a filtering or reduction process.  The numbers near the nodes likely track changes or maintain some form of ordering or identification throughout the process.
Input: n = 5, k = 2
Output: 2

Constraints:

In Plain English

New to this one? Start here. Below is the core idea in everyday language, plus a real-world analogy—then the sections that follow build on it with the formal reasoning, code, and step-by-step walkthroughs.

🥔 Real-world analogy: hot potato in a circle

It is the childhood game of hot potato: people stand in a circle and every k-th person is eliminated and leaves, then counting continues from the next person. The question is who is the last one standing — and tracking the survivor’s position after each round reveals a tidy formula.

One Big Idea

Simulating the circle is easy but slow. The clever insight: after the first elimination, we have the same game with n - 1 people — just renumbered. That gives the recurrence survivor(n) = (survivor(n - 1) + k) % n, solvable in a single O(n) loop.

Intuition

The naive approach to solving this problem is to simulate the removal of people step by step. We can create a circular linked list with n nodes. Starting from node 0, we iterate through the linked list, removing every kth person. The last node remaining after all removals will represent the last remaining person.

This approach takes O(nk)O(n\cdot k) time because, to remove a node, we must iterate through kk nodes in the linked list. Therefore, for each of the nn nodes, we perform kk iterations. Let's see if we can find a faster solution.

Consider an example where n = 12 and k = 4. For the first removal, we start counting k nodes from node 0 and remove the person we end up on after counting.

Image represents a circular arrangement of twelve numbered nodes (0 through 11) representing 'n people', with a central text label 'n people'.  An orange arrow labeled 'start' points downwards to node 0. A red curved arrow originates from node 0, passes over nodes 1 and 2, and terminates at node 3, which is marked with a red 'X' indicating its removal.  The red arrow is labeled 'k = 4', suggesting a process where every fourth person (starting from 0) is eliminated.  The numbers within the circles represent the positions of individuals in a circular arrangement, and the process depicted simulates a 'Josephus problem' type elimination.

Image represents a circular arrangement of twelve numbered circles (0 through 11) representing individuals, positioned around a central area.  The numbers are sequentially placed around the circle, starting with 0 at the top and proceeding clockwise.  The central area contains the text 'n - 1 people', indicating the number of people involved in a process, excluding one. An orange arrow points downwards from the text 'new start' to the circle labeled '4', signifying that the process begins anew with the individual represented by the number 4.  This suggests a cyclical or iterative process where the starting point shifts after each iteration, potentially involving the elimination or selection of individuals in a circular fashion.

As we can see, after the first removal, there is one less person in the circle. Additionally, after the removal, our new start position is at the kth position (person 4).

Now, we effectively need to find the last person remaining in a circle of n - 1 people, where we start counting at person k. This indicates that solving the subproblem josephus(n - 1, k) will help us get the answer to the problem josephus(n, k). Note that the answer to subproblem josephus(i, k) represents the last person standing in a circle of i people, where we start counting at person 0.

To account for the adjusted start position, we need to add k to the answer returned by josephus(n - 1, k). This is because, in this subproblem, it won’t know to start counting from position 4: it will, by default, count from position 0. So, adding k to this subproblem’s result accounts for this difference in the starting position.

This can be expressed with the following recurrence relation:

josephus(n, k) = josephus(n - 1, k) + k

The final consideration is ensuring the value of josephus(n - 1, k) + k doesn’t exceed n - 1, as this represents the position of the last person. We can achieve this by applying the modulus operator (% n) to josephus(n - 1, k) + k. This results in the following updated recurrence relation:

josephus(n, k) = (josephus(n - 1, k) + k) % n

Now, all we need is a base case.

Base case
The simplest version of this problem is when the circle contains only one person: n = 1. In this case, the last person remaining is person 0, so we just return person 0 for this base case.

Implementation


def josephus(n: int, k: int) -> int:
    # Base case: If there's only one person, the last person is person 0.
    if n == 1:
        return 0
    # Calculate the position of the last person remaining in the reduced problem
    # with 'n - 1' people. We use modulo 'n' to ensure the answer doesn't exceed
    # 'n - 1'.
    return (josephus(n - 1, k) + k) % n
int josephus(int n, int k) {
    queue<int> circle;
    for (int i = 0; i < n; i++) {
        circle.push(i);
    }
    while ((int)circle.size() > 1) {
        // Skip k - 1 people by moving them to the back.
        for (int s = 0; s < k - 1; s++) {
            circle.push(circle.front());
            circle.pop();
        }
        // The k-th person is now at the front: eliminate them.
        circle.pop();
    }
    return circle.front();
}
int josephus(int n, int k) {
    java.util.ArrayDeque<Integer> circle = new java.util.ArrayDeque<>();
    for (int i = 0; i < n; i++) {
        circle.add(i);
    }
    while (circle.size() > 1) {
        // Skip k - 1 people by moving them to the back.
        for (int s = 0; s < k - 1; s++) {
            circle.add(circle.poll());
        }
        // The k-th person is now at the front: eliminate them.
        circle.poll();
    }
    return circle.peek();
}

Complexity Analysis

Time complexity: The time complexity of josephus is O(n)O(n) because we make a total of nn recursive calls to this function until we reach the base case.

Space complexity: The space complexity is O(n)O(n) due to the recursive call stack, which grows up to a depth of nn.

Optimization

We can implement the top-down recursive solution above using a bottom-up iterative approach. Let res represent an array that stores the solution to each subproblem, where res[i] contains the solution to the subproblem of an i-person circle. Using this array, our formula becomes:

res[i] = (res[i - 1] + k) % i

The key observation here is that we only ever need access to the previous element of the res array (at i - 1) to calculate the result of the current subproblem (at i). This means we don’t need to store the entire array.

Instead, we can use a single variable to keep track of the solution to the previous subproblem. We can then update this variable to store the solution for the current subproblem:

res = (res + k) % i

Note that the 'res' value used on the right-hand side of the equation represents the previous subproblem’s result.

Implementation

def josephus_optimized(n: int, k: int) -> int:
    res = 0
    for i in range(2, n + 1):
        # res[i] = (res[i - 1] + k) % i.
        res = (res + k) % i
    return res
int josephusOptimized(int n, int k) {
    int survivor = 0;               // survivor for a circle of size 1
    for (int size = 2; size <= n; size++) {
        survivor = (survivor + k) % size;
    }
    return survivor;
}
int josephusOptimized(int n, int k) {
    int survivor = 0;               // survivor for a circle of size 1
    for (int size = 2; size <= n; size++) {
        survivor = (survivor + k) % size;
    }
    return survivor;
}

Complexity Analysis

Time complexity: The time complexity of josephus_optimized is O(n)O(n) because we iterate through nn subproblems.

Space complexity: The space complexity is O(1)O(1).

Dry Runs

Two step-by-step walkthroughs of the algorithm below: a valid case (the expected happy path) and an invalid / edge case that exercises the tricky parts. Each step shows the program state as the code runs—the same steps apply to Python, C++ and Java.

✅ Dry run 1 (valid) — n = 5, k = 2 with the recurrence.
Building the survivor up from a circle of 1.
sizeformulasurvivor
1base0
2(0 + 2) % 20
3(0 + 2) % 32
4(2 + 2) % 40
5(0 + 2) % 52

Survivor at index 2, matching the simulation (eliminate 1, 3, 0, 4). ✓

❌ Dry run 2 (edge) — n = 1, and k = 1.

n = 1: the loop never runs, so the survivor is index 0 — the only person. k = 1: every “first” person is removed in order 0, 1, 2, … so the last one, index n - 1, survives; the recurrence gives (0 + 1) % size = size − 1 at each step, ending at n − 1. ✗