Reverse Nodes in k-Group
HardPrompt
Given the head of a linked list, reverse the nodes of the list k at a time, and return the modified list.
k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes, in the end, should remain as it is.
You may not alter the values in the list's nodes, only nodes themselves may be changed.
Example 1:
Input: head = [1,2,3,4,5], k = 2
Output: [2,1,4,3,5]Example 2:
Input: head = [1,2,3,4,5], k = 3
Output: [3,2,1,4,5]
Constraints:
- The number of nodes in the list is
n. 1 <= k <= n <= 50000 <= Node.val <= 1000
Follow-up: Can you solve the problem in O(1) extra memory space?
Approaches
3 approaches with complexity analysis and trade-offs.
This approach uses a stack to reverse nodes in groups of k. We iterate through the linked list, and for each group of k nodes, we push them onto a stack. Then, we pop the nodes from the stack and relink them in reversed order. This process is repeated until the end of the list is reached. If the last group has fewer than k nodes, it is left unchanged.
Algorithm
- Create a
dummynode pointing toheadand aprevpointer todummy. - Create a stack to store nodes.
- Loop through the list:
- First, check if there are
knodes available starting fromprev.next. - If not, break the loop.
- If yes, push the
knodes onto the stack. - Pop nodes from the stack and relink them.
prev.nextwill point to the first popped node. Updateprevafter each pop. - After the group is reversed, link the tail of the reversed group to the start of the next group.
- First, check if there are
- Return
dummy.next.
Walkthrough
This method provides a straightforward way to reverse the groups by leveraging the Last-In, First-Out (LIFO) property of a stack.
Algorithm:
- Create a
dummynode that points to theheadof the list. This simplifies handling the list's head. - Initialize a
ppointer to thedummynode. This pointer will track the end of the previously reversed group. - Use a
whileloop to traverse the list. In each iteration, we first check if there are at leastknodes remaining. - If a group of
knodes exists, push theseknodes onto a stack. - Pop the nodes from the stack one by one and link them back into the list starting from
p.next. Updatepto point to the last node of the newly reversed group. - The
nextof the new tail of the group should point to the start of the next group. - If fewer than
knodes remain, the check at the beginning of the loop will fail, and we return the result. - Return
dummy.next.
import java.util.Stack; class Solution { public ListNode reverseKGroup(ListNode head, int k) { if (head == null || k == 1) return head; Stack<ListNode> stack = new Stack<>(); ListNode dummy = new ListNode(0); dummy.next = head; ListNode p = dummy; while (true) { ListNode check = p.next; int i = 0; for (; i < k; i++) { if (check == null) break; check = check.next; } if (i < k) break; // Not enough nodes ListNode temp = p.next; for (i = 0; i < k; i++) { stack.push(temp); temp = temp.next; } while (!stack.isEmpty()) { p.next = stack.pop(); p = p.next; } p.next = temp; } return dummy.next; }}Complexity
Time
O(N)
Space
O(k)
Trade-offs
Pros
Conceptually simple and easy to understand.
Leverages a standard data structure.
Cons
Uses O(k) extra space, which is not optimal and fails the follow-up constraint.
This approach solves the problem by breaking it down into smaller, self-similar subproblems. The function reverses the first k nodes and then recursively calls itself on the rest of the list. The reversed group is then linked to the result of the recursive call.
Algorithm
- Check if the list contains at least
knodes. If not, returnhead. - If it does, reverse the first
knodes of the list. - The original
headnode will become the tail of the reversed group. - Recursively call the function on the
(k+1)-th node. - Link the new tail's
nextpointer to the result of the recursive call. - Return the new head of the reversed group.
Walkthrough
Recursion provides an elegant way to handle the linked structure of the problem. The main idea is to handle one group at a time and delegate the rest of the list to a recursive call.
Algorithm:
- Define a base case: if the list has fewer than
knodes, no reversal is needed, so return the currenthead. To check this, we can first iterateknodes forward. If we hitnull, we are in the base case. - If there are at least
knodes, reverse the firstknodes of the list using the standard iterative reversal technique (withprev,curr,nextpointers). - After reversing, the original
headof the group becomes the tail. Thenextpointer of this new tail should point to the result of the recursive call on the rest of the list (reverseKGroup(next_node, k)). - The new head of the reversed group (which was the
k-th node originally) is returned.
class Solution { public ListNode reverseKGroup(ListNode head, int k) { // 1. Check if there are at least k nodes ListNode curr = head; int count = 0; while (curr != null && count < k) { curr = curr.next; count++; } // 2. If k nodes exist, reverse them if (count == k) { // Reverse the first k nodes ListNode prev = null; ListNode current = head; for (int i = 0; i < k; i++) { ListNode nextTemp = current.next; current.next = prev; prev = current; current = nextTemp; } // head is now the tail of the reversed group. // current is the head of the next part of the list. // Recursively call for the rest of the list and link. if (current != null) { head.next = reverseKGroup(current, k); } // prev is the new head of this reversed group return prev; } else { // 3. If less than k nodes, return head as is return head; } }}Complexity
Time
O(N)
Space
O(N/k)
Trade-offs
Pros
Elegant and concise code.
Clearly separates the logic for one group from the rest.
Cons
The recursion stack uses O(N/k) space, which can be significant for small
kand largeN.Not an O(1) space solution.
This is the most optimal approach, solving the problem iteratively with constant extra space. It processes the list in one pass, reversing each group of k nodes in place and carefully managing pointers to connect the reversed groups.
Algorithm
- Create a
dummynode pointing tohead. - Initialize
groupPrevtodummy. - Loop through the list:
- Find the
k-th node of the current group, let's call itkth. - If
kthis not found, break the loop. - Reverse the sublist between
groupPrev.nextandkth. - Connect
groupPrev.nextto the new head of the reversed group (the oldkthnode). - Update
groupPrevto point to the tail of the reversed group (the old start of the group).
- Find the
- Return
dummy.next.
Walkthrough
This method avoids both recursion and auxiliary data structures by using a few pointers to keep track of the different parts of the list during the reversal process.
Algorithm:
- Create a
dummynode and point itsnexttohead. This simplifies connections, especially for the first group. - Initialize
groupPrevtodummy. This pointer will always point to the node just before the current group being processed. - Enter a loop that continues as long as there are full groups of
knodes to reverse. - In each iteration, first find the
k-th node of the current group. Let's call itkth. Ifkthisnull, it means we have fewer thanknodes left, so we break the loop. - Identify the start of the current group (
groupStart = groupPrev.next) and the start of the next group (nextGroupStart = kth.next). - Reverse the sublist from
groupStarttokthin place. A common way is to treatnextGroupStartas theprevpointer's initial value in a standard reversal loop. - After reversal, the old
kthnode is the new head of the group, and the oldgroupStartis the new tail. - Connect the previous part of the list to this newly reversed group:
groupPrev.next = kth. - Update
groupPrevto be the tail of the just-reversed group (groupStart) to prepare for the next iteration. - Return
dummy.next.
class Solution { public ListNode reverseKGroup(ListNode head, int k) { if (head == null || k == 1) { return head; } ListNode dummy = new ListNode(0); dummy.next = head; ListNode groupPrev = dummy; while (true) { // 1. Find the k-th node of the current group ListNode kth = groupPrev; for (int i = 0; i < k && kth != null; i++) { kth = kth.next; } // If we don't have k nodes, we are done if (kth == null) { break; } // 2. Identify pointers for reversal and connection ListNode groupStart = groupPrev.next; ListNode nextGroupStart = kth.next; // 3. Reverse the k nodes in place ListNode prev = nextGroupStart; ListNode current = groupStart; while (current != nextGroupStart) { ListNode nextTemp = current.next; current.next = prev; prev = current; current = nextTemp; } // 4. Connect the reversed group groupPrev.next = kth; // 5. Update groupPrev for the next iteration groupPrev = groupStart; } return dummy.next; }}Complexity
Time
O(N)
Space
O(1)
Trade-offs
Pros
Most efficient solution with O(1) extra space.
Processes the list in a single pass without recursion.
Cons
The pointer manipulation can be complex and harder to reason about compared to the other approaches.
Solutions
Solution
/** * Definition for singly-linked list. * public class ListNode { * public int val; * public ListNode next; * public ListNode(int val=0, ListNode next=null) { * this.val = val; * this.next = next; * } * } */ public class Solution { public ListNode ReverseKGroup ( ListNode head , int k ) { ListNode dummy = new ListNode ( 0 , head ); ListNode pre = dummy , cur = dummy ; while ( cur . next != null ) { for ( int i = 0 ; i < k && cur != null ; ++ i ) { cur = cur . next ; } if ( cur == null ) { return dummy . next ; } ListNode t = cur . next ; cur . next = null ; ListNode start = pre . next ; pre . next = ReverseList ( start ); start . next = t ; pre = start ; cur = pre ; } return dummy . next ; } private ListNode ReverseList ( ListNode head ) { ListNode pre = null , p = head ; while ( p != null ) { ListNode q = p . next ; p . next = pre ; pre = p ; p = q ; } return pre ; } }public class Reverse_Nodes_in_k_Group { class Solution { public ListNode reverseKGroup ( ListNode head , int k ) { ListNode dummy = new ListNode ( 0 ); dummy . next = head ; ListNode prev = dummy ; // count total nodes ListNode tmp = head ; int count = 0 ; while ( tmp != null ) { count ++; tmp = tmp . next ; } // 1->2->3->4->5 , k=3 // 2,1,3,4,5 // 3,2,1,4,5 // => always getting 1's next for prev's next => current (below) not changing in one-batch-swap // if only one node left, then no swap while ( count >= k ) { ListNode originalFirst = prev . next ; int kcopy = k - 1 ; // @note: since current node is already counted as 1 while ( kcopy > 0 ) { // both prev and current, not changed in while loop ListNode nextNextCopy = originalFirst . next . next ; ListNode firstInGroup = prev . next ; prev . next = originalFirst . next ; prev . next . next = firstInGroup ; originalFirst . next = nextNextCopy ; kcopy --; } // @note: update previous AND current. I forgot current... prev = originalFirst ; // now current is the last one of this group count -= k ; } return dummy . 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 reverseKGroup ( ListNode head , int k ) { ListNode dummy = new ListNode ( 0 , head ); ListNode pre = dummy , cur = dummy ; while ( cur . next != null ) { for ( int i = 0 ; i < k && cur != null ; ++ i ) { cur = cur . next ; } if ( cur == null ) { return dummy . next ; } ListNode t = cur . next ; cur . next = null ; ListNode start = pre . next ; pre . next = reverseList ( start ); start . next = t ; pre = start ; cur = pre ; } return dummy . next ; } private ListNode reverseList ( ListNode head ) { ListNode pre = null , p = head ; while ( p != null ) { ListNode q = p . next ; p . next = pre ; pre = p ; p = q ; } return pre ; } }# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution : def reverseKGroup ( self , head : ListNode , k : int ) -> ListNode : ''' for reverse 1->2->3->4->5, process is like 1->None, 2->3->4->5 2->1->None, 3->4->5 3->2->1->None, 4->5 4->3->2->1->None, 5 5->4->3->2->1->None, None ''' def reverseList ( head ): pre , p = None , head while p : pnext = p . next p . next = pre pre = p p = pnext return pre dummy = ListNode ( next = head ) pre = cur = dummy while cur . next : for _ in range ( k ): cur = cur . next if cur is None : return dummy . next t = cur . next cur . next = None # cut from next k-group, so to reverseList() for current k-group start = pre . next pre . next = reverseList ( start ) start . next = t # so now 'start' is the last node of k-group pre = cur = start # same as the reset dummy before while loop return dummy . next ############ # Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution ( object ): def reverseKGroup ( self , head , k ): """ :type head: ListNode :type k: int :rtype: ListNode """ def reverseList ( head , k ): pre = None cur = head while cur and k > 0 : tmp = cur . next cur . next = pre pre = cur cur = tmp k -= 1 head . next = cur return cur , pre length = 0 p = head while p : length += 1 p = p . next if length < k : return head step = length / k ret = None pre = None p = head while p and step : next , newHead = reverseList ( p , k ) if ret is None : ret = newHead if pre : pre . next = newHead pre = p p = next step -= 1 return ret// OJ: https://leetcode.com/problems/reverse-nodes-in-k-group/ // Time: O(N) // Space: O(1) class Solution { public: ListNode * reverseKGroup ( ListNode * head , int k ) { ListNode h , * tail = & h ; while ( head ) { auto prev = tail ; int i = 0 ; for ( auto p = head ; i < k && p ; ++ i , p = p -> next ); if ( i < k ) { tail -> next = head ; break ; } for ( int i = 0 ; i < k && head ; ++ i ) { auto node = head ; head = head -> next ; node -> next = prev -> next ; prev -> next = node ; } while ( tail -> next ) tail = tail -> next ; } return h . next ; } };Video 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.