Josephus Problem
Problem Statement
Write a Java program to solve the Josephus problem using a circular linked list. The Josephus problem involves n people standing in a circle, numbered from 1 to n, where every k-th person is eliminated until only one remains. The circular linked list is a singly linked list where the last node’s next pointer points to the head, forming a cycle. The program should return the value of the last person remaining and test the solution with different values of k and list sizes, including edge cases like single-person lists and k=1. You can visualize this as a circle of numbered cards where every k-th card is removed until only one card remains.
Input:
- A circular linked list of integers representing people (e.g., 1→2→3→4→1 for n=4) and an integer k (the step size for elimination). Output: The value of the last person remaining (e.g., 3 for n=4, k=2). Constraints:
- The list size n is between 1 and 10^5.
- Node values are integers from 1 to n.
- k is a positive integer (k ≥ 1). Example:
- Input: List = 1→2→3→4→1, k = 2
- Output: 3
- Explanation: Eliminate every 2nd person: 2, 4, 1, leaving 3.
- Input: List = 1→1, k = 1
- Output: 1
- Explanation: Single person remains.
- Input: List = 1→2→3→1, k = 3
- Output: 1
- Explanation: Eliminate every 3rd person: 3, 2, leaving 1.
Pseudocode
CLASS Node
SET value to integer
SET next to Node (null by default)
ENDCLASS
FUNCTION solveJosephus(head, k)
IF head is null THEN
RETURN -1
ENDIF
IF head.next is head THEN
RETURN head.value
ENDIF
SET current to head
WHILE current.next is not current
FOR i from 1 to k-1
SET current to current.next
ENDFOR
SET nextNode to current.next
SET current.next to nextNode.next
SET current to current.next
ENDWHILE
RETURN current.value
ENDFUNCTION
FUNCTION toString(head)
IF head is null THEN
RETURN "[]"
ENDIF
CREATE result as new StringBuilder
SET current to head
SET visited as new Set
WHILE current is not null AND current not in visited
ADD current to visited
APPEND current.value to result
IF current.next is not head THEN
APPEND " " to result
ENDIF
SET current to current.next
ENDWHILE
RETURN result as string
ENDFUNCTION
FUNCTION main()
SET testCases to array of test cases (list values, k)
FOR each testCase in testCases
PRINT test case details
CALL solveJosephus(testCase.head, testCase.k)
PRINT last person remaining
ENDFOR
ENDFUNCTION
Algorithm Steps
- Define a
Nodeclass with: a. An integervalue(person number). b. Anextpointer to the next node. - In
solveJosephus: a. If the list is empty, return -1. b. If the list has one node, return its value. c. Setcurrentto head. d. While more than one node remains:- Traverse k-1 steps to find the node before the one to eliminate.
- Remove the k-th node by updating
current.nextto skip it. - Move
currentto the next node. e. Return the value of the last remaining node.
- In
toString: a. If head is null, return "[]". b. Traverse from head, use a Set to avoid cycling, append values with spaces. c. Return the string representation. - In
main, test with different list sizes (n=1, n>1) and k values (k=1, k>1), including edge cases.
Java Implementation
import java.util.*;
public class JosephusProblem {
// Node class for the circular linked list
static class Node {
int value;
Node next;
Node(int value) {
this.value = value;
this.next = null;
}
}
// Solves the Josephus problem
public int solveJosephus(Node head, int k) {
if (head == null) {
return -1;
}
if (head.next == head) {
return head.value;
}
Node current = head;
while (current.next != current) {
// Move to the node before the one to be eliminated
for (int i = 0; i < k - 1; i++) {
current = current.next;
}
// Remove the k-th node
Node nextNode = current.next;
current.next = nextNode.next;
current = current.next;
}
return current.value;
}
// Converts circular linked list to string
public String toString(Node head) {
if (head == null) {
return "[]";
}
StringBuilder result = new StringBuilder();
Node current = head;
Set<Node> visited = new HashSet<>();
while (current != null && !visited.contains(current)) {
visited.add(current);
result.append(current.value);
if (current.next != head) {
result.append(" ");
}
current = current.next;
}
return result.toString();
}
// Helper class for test cases
static class TestCase {
Node head;
int k;
TestCase(int n, int k) {
this.k = k;
if (n == 0) {
head = null;
return;
}
head = new Node(1);
Node current = head;
for (int i = 2; i <= n; i++) {
current.next = new Node(i);
current = current.next;
}
// Make circular
current.next = head;
}
}
// Main method to test Josephus problem
public static void main(String[] args) {
JosephusProblem solver = new JosephusProblem();
// Test cases
TestCase[] testCases = {
new TestCase(4, 2), // n=4, k=2
new TestCase(3, 3), // n=3, k=3
new TestCase(1, 1), // Single node
new TestCase(5, 1), // k=1
new TestCase(7, 4) // Larger n, k
};
// Run test cases
for (int i = 0; i < testCases.length; i++) {
TestCase test = testCases[i];
System.out.println("Test case " + (i + 1) + ":");
System.out.println("Input list: " + solver.toString(test.head));
System.out.println("k: " + test.k);
int result = solver.solveJosephus(test.head, test.k);
System.out.println("Last person remaining: " + result + "\n");
}
}
}
Output
Running the main method produces:
Test case 1:
Input list: 1 2 3 4
k: 2
Last person remaining: 3
Test case 2:
Input list: 1 2 3
k: 3
Last person remaining: 1
Test case 3:
Input list: 1
k: 1
Last person remaining: 1
Test case 4:
Input list: 1 2 3 4 5
k: 1
Last person remaining: 5
Test case 5:
Input list: 1 2 3 4 5 6 7
k: 4
Last person remaining: 2
Explanation:
- Test case 1: n=4, k=2, eliminate 2, 4, 1, leaving 3.
- Test case 2: n=3, k=3, eliminate 3, 2, leaving 1.
- Test case 3: n=1, k=1, single node 1 remains.
- Test case 4: n=5, k=1, eliminate 1, 2, 3, 4, leaving 5.
- Test case 5: n=7, k=4, eliminate 4, 1, 6, 5, 7, 3, leaving 2.
How It Works
- Node: Stores an integer value (person number) and a
nextpointer. - solveJosephus:
- Returns -1 for empty lists, head value for single-node lists.
- Iterates while more than one node remains:
- Traverses k-1 steps to find the node before the k-th.
- Removes k-th node by updating
nextpointer. - Moves to the next node.
- Returns the value of the last node.
- toString: Traverses from head, uses a Set to prevent cycling, returns space-separated string or "[]".
- Example Trace (Test case 1):
- Input: 1→2→3→4→1, k=2.
- Step 1: current=1, eliminate 2, list=1→3→4→1.
- Step 2: current=3, eliminate 4, list=1→3→1.
- Step 3: current=1, eliminate 1, list=3→3.
- Return: 3.
- Main Method: Tests different n and k, including single node and k=1.
Complexity Analysis Table
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Solve Josephus | O(n * k) | O(1) |
| To String | O(n) | O(n) |
Note:
- n is the number of nodes in the list.
- Time complexity: O(n * k) for solveJosephus (n-1 eliminations, each up to k steps); O(n) for toString (traverse list).
- Space complexity: O(1) for solveJosephus (constant pointers); O(n) for toString (StringBuilder and Set).
- Worst case: O(n * k) time, O(n) space for output with large lists and k.
✅ Tip: Use the circular linked list’s structure to efficiently eliminate nodes by updating
nextpointers. Keep track of the current node to continue from the correct position after each elimination.
⚠ Warning: Ensure k is handled correctly for each elimination step, and avoid infinite loops by checking for single-node conditions. Handle edge cases like n=1 or empty lists.