RECURSION & BACKTRACKING › RECURSION PROBLEMS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →Directly removing people from a circle forces you to maintain the remaining order and repeatedly find the next position. With n as large as 1,000,000, that simulation can spend too much time shifting or searching. The useful observation is that you do not need the entire removal order; you only need the final survivor's position.
Use zero-based positions first. A circle containing one person has survivor index 0. Suppose you already know the survivor index for a circle of size size - 1. Adding one person creates a circle of size size, and the first removal shifts the reference point by k positions. Therefore, the old survivor moves to index (old survivor + k) modulo size. Repeating this from size 2 through n builds the answer without storing the circle.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The loop performs one constant-time update for each circle size from 2 through n. Each update discards the previous circle representation, so no person is searched for, removed, or shifted repeatedly. |
| Space | O(1) extra | Only the current survivor index and loop variable are stored; the returned label is output storage and is excluded. The bound stays constant even for the largest or most uneven removal pattern because no circle or recursion stack is allocated. |
#include <vector>
using namespace std;
class Solution {
public:
int josephus(int n, int k) {
long long survivor = 0;
for (int size = 2; size <= n; ++size) {
survivor = (survivor + (long long)k) % size;
}
return (int)survivor + 1;
}
};The loop invariant is the key line of reasoning: immediately before processing size, survivor is the zero-based survivor for a circle of size - 1. Adding k and taking modulo size transforms that known position into the survivor position for the new circle. The final conversion is deliberately outside the loop, so every recurrence remains in zero-based coordinates.
#include <vector>
using namespace std;
class Solution {
public:
int josephus(int n, int k) {
return solve(n, k) + 1;
}
private:
long long solve(int size, int k) {
if (size == 1) return 0;
return (solve(size - 1, k) + (long long)k) % size;
}
};This is a different arrangement, not an optimisation. It mirrors the mathematical recurrence clearly, but it creates one call frame for every circle size, so its extra space is O(n) and a large n can exhaust the runtime stack. The iterative solution keeps the same O(n) time while reducing working space to O(1), which is why it is the safer submission here.