Split Circular List
Problem Statement
Write a Java program that splits a circular linked list into two circular linked lists of roughly equal size. A circular linked list is a singly linked list where the last node’s next pointer points to the head, forming a cycle. The splitting should divide the nodes as evenly as possible, with one list having at most one more node than the other for odd-sized lists, and each resulting list maintaining its circular structure. Test the implementation with even-sized lists, odd-sized lists, empty lists, and single-node lists. You can visualize this as dividing a ring of numbered beads into two smaller rings, each containing about half the beads, with both rings remaining closed loops.
Input:
- A circular linked list of integers (e.g., 1→2→3→4→1, where 4→1 forms the cycle). Output: Two circular linked lists as strings, listing nodes from each head until just before it cycles (e.g., "1 2" and "3 4" for the input above). Constraints:
- The list size is between 0 and 10^5.
- Node values are integers in the range [-10^9, 10^9].
- The list may be empty. Example:
- Input: List = 1→2→3→4→1
- Output: First list: "1 2", Second list: "3 4"
- Explanation: Splits into two circular lists of 2 nodes each: 1→2→1 and 3→4→3.
- Input: List = 1→2→3→1
- Output: First list: "1 2", Second list: "3"
- Explanation: Splits into 1→2→1 (2 nodes) and 3→3 (1 node).
- Input: List = []
- Output: First list: "[]", Second list: "[]"
- Explanation: Empty list splits into two empty lists.
Pseudocode
CLASS Node
SET value to integer
SET next to Node (null by default)
ENDCLASS
FUNCTION splitList(head)
IF head is null THEN
RETURN (null, null)
ENDIF
IF head.next is head THEN
RETURN (head, null)
ENDIF
SET length to 1
SET tail to head
WHILE tail.next is not head
INCREMENT length
SET tail to tail.next
ENDWHILE
SET splitPoint to length / 2
SET current to head
FOR i from 1 to splitPoint - 1
SET current to current.next
ENDFOR
SET firstHead to head
SET secondHead to current.next
SET secondTail to tail
SET firstTail to current
SET firstTail.next to firstHead
SET secondTail.next to secondHead
RETURN (firstHead, secondHead)
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 circular linked lists
FOR each testCase in testCases
PRINT test case details
CALL splitList(testCase.head)
PRINT first and second lists using toString
ENDFOR
ENDFUNCTION
Algorithm Steps
- Define a
Nodeclass with: a. An integervalue. b. Anextpointer to the next node. - In
splitList: a. If the list is empty, return (null, null). b. If the list has one node, return (head, null). c. Find the list length and tail by traversing untiltail.nextis head. d. Compute split point aslength / 2. e. Traverse to the node before the split point (first list’s tail). f. Set first list’s head and tail, second list’s head and tail. g. Connect first list’s tail to its head, second list’s tail to its head. h. Return (firstHead, secondHead). - 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 even-sized, odd-sized, empty, and single-node circular lists.
Java Implementation
import java.util.*;
public class SplitCircularLinkedList {
// Node class for the circular linked list
static class Node {
int value;
Node next;
Node(int value) {
this.value = value;
this.next = null;
}
}
// Splits the circular linked list into two circular lists
public Node[] splitList(Node head) {
Node[] result = new Node[2];
if (head == null) {
result[0] = null;
result[1] = null;
return result;
}
if (head.next == head) {
result[0] = head;
result[1] = null;
return result;
}
// Find length and tail
int length = 1;
Node tail = head;
while (tail.next != head) {
length++;
tail = tail.next;
}
// Find split point
int splitPoint = length / 2;
Node current = head;
for (int i = 0; i < splitPoint - 1; i++) {
current = current.next;
}
// Set heads and tails
Node firstHead = head;
Node secondHead = current.next;
Node firstTail = current;
Node secondTail = tail;
// Form two circular lists
firstTail.next = firstHead;
secondTail.next = secondHead;
result[0] = firstHead;
result[1] = secondHead;
return result;
}
// 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;
TestCase(int[] values) {
if (values.length == 0) {
head = null;
return;
}
head = new Node(values[0]);
Node current = head;
for (int i = 1; i < values.length; i++) {
current.next = new Node(values[i]);
current = current.next;
}
// Make circular
current.next = head;
}
}
// Main method to test circular linked list splitting
public static void main(String[] args) {
SplitCircularLinkedList splitter = new SplitCircularLinkedList();
// Test cases
TestCase[] testCases = {
new TestCase(new int[]{1, 2, 3, 4}), // Even-sized list
new TestCase(new int[]{1, 2, 3}), // Odd-sized list
new TestCase(new int[]{}), // Empty list
new TestCase(new int[]{5}), // Single node
new TestCase(new int[]{1, 1, 1, 1, 1}) // Odd-sized with duplicates
};
// 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: " + splitter.toString(test.head));
Node[] result = splitter.splitList(test.head);
System.out.println("First list: " + splitter.toString(result[0]));
System.out.println("Second list: " + splitter.toString(result[1]) + "\n");
}
}
}
Output
Running the main method produces:
Test case 1:
Input list: 1 2 3 4
First list: 1 2
Second list: 3 4
Test case 2:
Input list: 1 2 3
First list: 1 2
Second list: 3
Test case 3:
Input list: []
First list: []
Second list: []
Test case 4:
Input list: 5
First list: 5
Second list: []
Test case 5:
Input list: 1 1 1 1 1
First list: 1 1 1
Second list: 1 1
Explanation:
- Test case 1: Splits 1→2→3→4→1 into 1→2→1 (2 nodes) and 3→4→3 (2 nodes).
- Test case 2: Splits 1→2→3→1 into 1→2→1 (2 nodes) and 3→3 (1 node).
- Test case 3: Empty list splits into "[]" and "[]".
- Test case 4: Single node 5→5 splits into 5→5 and "[]".
- Test case 5: Splits 1→1→1→1→1→1 into 1→1→1→1 (3 nodes) and 1→1→1 (2 nodes).
How It Works
- Node: Stores an integer value and a
nextpointer. - splitList:
- Returns (null, null) for empty lists, (head, null) for single-node lists.
- Finds list length and tail by traversing to
tail.next=head. - Computes split point as
length / 2. - Traverses to the node before the split point (first list’s tail).
- Sets first list’s head/tail and second list’s head/tail.
- Connects first tail to first head, second tail to second head.
- 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.
- length=4, tail=4, splitPoint=2.
- current=2 (after 1 step), firstHead=1, firstTail=2, secondHead=3, secondTail=4.
- firstTail.next=firstHead: 1→2→1.
- secondTail.next=secondHead: 3→4→3.
- Returns (1, 3), outputs "1 2" and "3 4".
- Main Method: Tests even-sized, odd-sized, empty, single-node, and duplicate lists.
Complexity Analysis Table
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Split List | O(n) | O(1) |
| To String | O(n) | O(n) |
Note:
- n is the number of nodes in the list.
- Time complexity: O(n) for splitList (traverse to find length and split point); O(n) for toString (traverse list).
- Space complexity: O(1) for splitList (constant pointers); O(n) for toString (StringBuilder and Set).
- Worst case: O(n) time, O(n) space for output with large lists.
✅ Tip: Split