Odd Even Linked List
MedPrompt
Given the head of a singly linked list, group all the nodes with odd indices together followed by the nodes with even indices, and return the reordered list.
The first node is considered odd, and the second node is even, and so on.
Note that the relative order inside both the even and odd groups should remain as it was in the input.
You must solve the problem in O(1) extra space complexity and O(n) time complexity.
Example 1:
Input: head = [1,2,3,4,5]
Output: [1,3,5,2,4]Example 2:
Input: head = [2,1,3,5,6,4,7]
Output: [2,3,6,7,1,5,4]
Constraints:
- The number of nodes in the linked list is in the range
[0, 104]. -106 <= Node.val <= 106
Approaches
2 approaches with complexity analysis and trade-offs.
A straightforward way to solve this problem is to conceptually split the original list into two separate lists: one containing all the odd-indexed nodes and another containing all the even-indexed nodes. We can achieve this by iterating through the list and appending each node to the appropriate list. To make list construction easier, we use dummy head nodes for both the odd and even lists. After the traversal, we link the tail of the odd list to the head of the even list.
Algorithm
- Handle the edge case: If
headis null, returnnull. - Create two dummy nodes,
oddHeadandevenHead, to serve as heads for the odd and even lists. - Create tail pointers,
oddTailandevenTail, initialized to the dummy heads. - Use a boolean flag
isOdd, initialized totrue, to track whether the current node belongs to the odd or even group. - Iterate through the original list with a
currpointer starting fromhead. - Inside the loop:
- If
isOddis true, appendcurrto the odd list and advanceoddTail. - Otherwise, append
currto the even list and advanceevenTail. - Move
currto the next node and flip theisOddflag.
- If
- After the loop, connect the tail of the odd list to the head of the even list (
oddTail.next = evenHead.next). - Crucially, terminate the now-combined list by setting the tail of the even part to null (
evenTail.next = null). - Return the head of the modified list, which is
oddHead.next.
Walkthrough
We start by creating two dummy nodes, oddHead and evenHead, which will act as sentinel nodes for our odd and even lists. We also maintain oddTail and evenTail pointers to keep track of the last node in each list, allowing for O(1) appends.
We iterate through the original list using a curr pointer and a boolean flag isOdd to determine where to place the current node. If isOdd is true, we append the node to the odd list; otherwise, we append it to the even list. We then advance curr and toggle the flag.
After processing all nodes, the original list structure is effectively partitioned into two chains of nodes. We must terminate the even list by setting evenTail.next to null. Then, we connect the two lists by setting oddTail.next to the first node of the even list, which is evenHead.next.
The head of the reordered list is oddHead.next.
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */class Solution { public ListNode oddEvenList(ListNode head) { if (head == null) { return null; } ListNode oddHead = new ListNode(0); ListNode evenHead = new ListNode(0); ListNode oddTail = oddHead; ListNode evenTail = evenHead; ListNode curr = head; boolean isOdd = true; while (curr != null) { if (isOdd) { oddTail.next = curr; oddTail = oddTail.next; } else { evenTail.next = curr; evenTail = evenTail.next; } curr = curr.next; isOdd = !isOdd; } // Connect the odd list with the even list oddTail.next = evenHead.next; // Terminate the combined list evenTail.next = null; return oddHead.next; }}Complexity
Time
O(n), where n is the number of nodes in the linked list. We iterate through the list once.
Space
O(1) extra space. We only use a constant number of extra pointers (`oddHead`, `evenHead`, `oddTail`, `evenTail`, `curr`) regardless of the input list's size.
Trade-offs
Pros
Conceptually simple and easy to understand.
Clearly separates the logic for handling odd and even nodes.
Cons
Uses dummy nodes, which adds a small amount of memory and code overhead.
The logic involves more pointer variables compared to a more optimized in-place solution.
This is the most efficient approach, which modifies the linked list in-place without using any dummy nodes. The idea is to maintain two pointers, one for the last node of the odd-indexed group (odd) and one for the last node of the even-indexed group (even). We iterate through the list, weaving the odd and even nodes into their respective groups by rearranging the next pointers.
Algorithm
- If
headis null, returnnull. - Initialize
oddpointer tohead. - Initialize
evenpointer tohead.next. - Save the head of the even list:
evenHead = even. - Loop as long as
evenandeven.nextare not null.- Set
odd.nexttoeven.nextto link the next odd node. - Move
oddtoodd.next. - Set
even.nexttoodd.nextto link the next even node. - Move
eventoeven.next.
- Set
- After the loop, link the tail of the odd list to the head of the even list:
odd.next = evenHead. - Return the original
head.
Walkthrough
First, we handle the edge cases where the list is empty or has fewer than three nodes, as no reordering is needed. We initialize an odd pointer to head (the first odd node) and an even pointer to head.next (the first even node). We also save a reference to the head of the even list, evenHead = head.next, because we'll need to connect the end of the odd list to it later.
We then enter a loop that continues as long as there are nodes to process, which is checked by even != null && even.next != null. This condition ensures we always have a pair of nodes (an even one followed by an odd one) to rearrange.
Inside the loop:
- We link the current
oddnode to the next odd node, which iseven.next. So,odd.next = even.next;. - We advance the
oddpointer to this new node:odd = odd.next;. - We link the current
evennode to the next even node, which is nowodd.next. So,even.next = odd.next;. - We advance the
evenpointer to this new node:even = even.next;.
After the loop terminates, the odd pointer will be at the tail of the odd-node sublist. We connect it to the head of the even-node sublist: odd.next = evenHead;. Finally, we return the original head.
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */class Solution { public ListNode oddEvenList(ListNode head) { if (head == null) { return null; } ListNode odd = head; ListNode even = head.next; ListNode evenHead = even; // Save the head of the even list // Loop while there's a pair of nodes (even and its successor) to process while (even != null && even.next != null) { // Link the next odd node odd.next = even.next; odd = odd.next; // Link the next even node even.next = odd.next; even = even.next; } // Connect the end of the odd list to the head of the even list odd.next = evenHead; return head; }}Complexity
Time
O(n), where n is the number of nodes. We traverse the list once, and each node is visited a constant number of times.
Space
O(1) extra space. We only use a few extra pointers (`odd`, `even`, `evenHead`). This is a true in-place algorithm.
Trade-offs
Pros
Highly efficient in both time and space, meeting the problem constraints perfectly.
Modifies the list in-place without needing extra data structures like dummy nodes.
The code is concise and elegant.
Cons
The pointer manipulation can be slightly harder to visualize and reason about compared to the separate lists approach.
Solutions
Solution
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public ListNode oddEvenList ( ListNode head ) { if ( head == null ) { return null ; } ListNode a = head ; ListNode b = head . next , c = b ; while ( b != null && b . next != null ) { a . next = b . next ; a = a . next ; b . next = a . next ; b = b . next ; } a . next = c ; return head ; } }/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode * oddEvenList ( ListNode * head ) { if ( ! head ) { return nullptr ; } ListNode * a = head ; ListNode * b = head -> next , * c = b ; while ( b && b -> next ) { a -> next = b -> next ; a = a -> next ; b -> next = a -> next ; b = b -> next ; } a -> next = c ; return head ; } };# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution : def oddEvenList ( self , head : Optional [ ListNode ]) -> Optional [ ListNode ]: if head is None : return None a = head b = c = head . next while b and b . next : a . next = b . next a = a . next b . next = a . next b = b . next a . next = c return headVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.