Solution: Reverse Nodes in k-Group
Let's solve the Reverse Nodes in k-Group problem using the In-Place Manipulation of a Linked List pattern.
Statement
The task is to reverse the nodes in groups of in a given linked list, where is a positive integer, and at most the length of the linked list. If any remaining nodes are not part of a group of , they should remain in their original order.
It is not allowed to change the values of the nodes in the linked list. Only the order of the nodes can be modified.
Note: Use only extra memory space.
Constraints:
Let n
be the number of nodes in a linked list.
-
k
n
-
Node.value
Solution
So far, you’ve probably brainstormed some approaches and have an idea of how to solve this problem. Let’s explore some of these approaches and figure out which one to follow based on considerations such as time complexity and any implementation constraints.
Naive approach
A naive approach would be to use another data structure—like a stack—to reverse the nodes of the linked list and then create a new linked list with reversed nodes. Here’s how the algorithm works:
-
We iterate the linked list.
-
We push the group of nodes to the stack.
-
We pop all numbers of nodes from the stack and add the nodes to a new linked list. When we do this, the stack will give us the reversed nodes in the group.
-
We repeat the above steps for every group of size present in our linked list.
-
In the end, if there are less than nodes left in the original linked list, we’ll point the tail of the reversed linked list to the remaining nodes of the original linked list.
The time complexity of this solution is , since we traverse the linked list once. However, the space complexity is , where is the length of the linked list to store the reversed elements and is the length of the stack. If a linked list contains thousands of nodes, we need to allocate a lot of memory resources to solve this problem. Let’s see if we can use the in-place linked list manipulation pattern to reduce the space complexity of our solution.
Optimized approach using in-place manipulation of a linked list
This approach optimizes space by reversing groups of nodes directly within the linked list, treating each group as a mini-linked list for in-place reversal. The approach progresses by first identifying contiguous groups of exactly nodes. Upon finding such a group, it reverses the nodes within the group in place, ensuring an efficient reorganization without using extra memory. After each reversal, the algorithm reattaches the reversed group segment back to the body of the list, maintaining the overall remaining structure. This process is repeated until it encounters a segment with fewer than nodes.
The slides below illustrate how we would like the algorithm to run:
Note: In the following section, we will gradually build the solution. Alternatively, you can skip straight to just the code.
Step-by-step solution construction
We will first traverse the linked list and check which groups of nodes can be reversed. Here is how the algorithm works:
-
We initialize a node,
dummy
, and attach it to the start of the linked list, i.e., by setting itsnext
pointer equal to the head. -
We set a pointer,
ptr
, equal to thedummy
node. We will use this pointer to traverse the linked list. -
We traverse the linked list till
ptr
becomes NULL:-
We initialize a pointer,
tracker
, toptr
. This pointer will be used to keep track of the number of nodes in the current group in the linked list. -
We use a nested loop to try to move
tracker
nodes forward in the linked list. Iftracker
becomes NULL before moving nodes forward, the end of the linked list has been reached and the current group can not be traversed, since it contains less than nodes. Therefore, we break out of the nested loop. Otherwise, the current group contains nodes andtracker
will point to the node of the current group.
-
-
After the completion of the nested loop, we check if
tracker
points to NULL:-
If it does, we’ve reached the end of the linked list. The current group contains less than nodes and cannot be reversed. Therefore, we break out of the outer loop, and the algorithm ends.
-
If it does not, the current group contains nodes and can therefore be reversed.
-
class Reorder {public static void reverseKGroups(LinkedListNode head, int k) {// Create a dummy node and set its next pointer to the headLinkedListNode dummy = new LinkedListNode(0);dummy.next = head;LinkedListNode ptr = dummy;while (ptr != null) {System.out.println("\tIdentifying a group of " + k + " nodes:");System.out.println("\t\tptr: " + ptr.data);// Keep track of the current positionLinkedListNode tracker = ptr;System.out.print("\t\tCurrent group: ");// Traverse k nodes to check if there are enough nodes to reversefor (int i = 0; i < k; i++) {if (tracker == null) {break;}tracker = tracker.next;if (tracker != null) {System.out.print(tracker.data + " ");} else {System.out.print("");}}// If there are not enough nodes to reverse, break out of the loopif (tracker == null) {System.out.println("\n\t\tThe above group contains less than " + k + " nodes, so we can't reverse it.\n");break;} else {System.out.println("\n\t\tThe above group of " + k + " nodes can be reversed.\n");}ptr = tracker;}}public static void main(String[] args) {int[] inputList = {1, 2, 3, 4, 5, 6, 7, 8};int k = 3;LinkedList inputLinkedList = new LinkedList();inputLinkedList.createLinkedList(inputList);System.out.print("Linked list: ");PrintList.printListWithForwardArrow(inputLinkedList.head);System.out.print("\n");System.out.println();reverseKGroups(inputLinkedList.head, k);}}
Note: The
printListWithForwardArrow
method is used to print the linked list with arrows. It isn’t part of the solution logic.
The next step is to reverse the first group of nodes. Here is how the algorithm works:
-
We modify the existing
reverseLinkedList
function in theLinkedListReversal.java
file to take an additional parameter,k
, which specifies the number of nodes to reverse. -
In the
reverseLinkedList
function, for the case wheretracker
does not point to NULL, we declare three pointers:-
current
-
previous
-
next
-
-
We call the
reverseLinkedList
function, which reverses the current group of nodes and updates the above three pointers by returning their values. -
After the reversal, we have a fragmented group that has been separated from the rest of the list. The
previous
pointer now points to the first node of the reversed group while thecurrent
andnext
pointers now point to the first node of the next group. -
We break out of the outer loop to end the algorithm once the first group has been reversed.
Note: Changes were made in the following two files:
main.java
LinkedListReversal.java
class LinkedListReversal{static LinkedListNode[] reverseLinkedList(LinkedListNode node, int k){LinkedListNode previous = null;LinkedListNode current = node;LinkedListNode next = null;for (int i = 0; i < k; i++) {// temporarily store the next nodenext = current.next;// reverse the current nodecurrent.next = previous;// before we move to the next node, point previous to the// current nodeprevious = current;// move to the next nodecurrent = next;}System.out.println("\t\tPointers after reversing k = " + k + " elements:");System.out.println("\t\t\tcurrent: " + (current != null ? Integer.toString(current.data) : "null"));System.out.println("\t\t\tnext: " + (next != null ? Integer.toString(next.data) : "null"));System.out.println("\t\t\tprevious: " + (previous != null ? Integer.toString(previous.data) : "null"));return new LinkedListNode[]{previous, current};}}
After reversing the first group of nodes, we need to reattach it to the rest of the linked list. Here is how the algorithm works:
-
We first need to access the last node in the reversed group. The
ptr
pointer is currently pointing to the node immediately before the last node of the reversed group. We initialize a new pointer,lastNodeOfReversedGroup
, and set it equal to the next node ofptr
. This node now points to the last node of the reversed group. -
We now need to link the last node of the reversed group to the first node of the linked list coming after it. The
current
pointer is currently pointing to the first node of the next group. We set the next node oflastNodeOfReversedGroup
to thecurrent
pointer. -
We now need to link the first node of the reversed group to the last node of the linked list that comes before it. The
previous
node is currently pointing to the first node of the reversed group. We set the next node ofptr
equal to theprevious
pointer. -
Lastly, we need to set the
ptr
pointer equal to the last node of the reversed group, which resets its position so that we can attempt to reverse the next group. We do this by setting theptr
pointer equal to thelastNodeOfReversedGroup
pointer. -
We break out of the outer loop to end the algorithm once the first reversed group has been reattached to the linked list.
class Reorder {public static void reverseKGroups(LinkedListNode head, int k) {// Create a dummy node and set its next pointer to the headLinkedListNode dummy = new LinkedListNode(0);dummy.next = head;LinkedListNode ptr = dummy;while (ptr != null) {System.out.println("\tIdentifying a group of " + k + " nodes:");System.out.println("\t\tptr: " + ptr.data);// Keep track of the current positionLinkedListNode tracker = ptr;System.out.print("\t\tCurrent group: ");// Traverse k nodes to check if there are enough nodes to reversefor (int i = 0; i < k; i++) {if (tracker == null) {break;}tracker = tracker.next;if (tracker != null) {System.out.print(tracker.data + " ");} else {System.out.print("");}}// If there are not enough nodes to reverse, break out of the loopif (tracker == null) {System.out.println();System.out.println("\t\tThe above group contains less than " + k + " nodes, so we can't reverse it.\n");break;}System.out.println();System.out.println("\t\tThe above group of " + k + " nodes can be reversed.\n");// Reverse the current group of k nodesSystem.out.println("\tReversing the current group of " + k + " nodes:");LinkedListNode[] updatedNodes = LinkedListReversal.reverseLinkedList(ptr.next, k);LinkedListNode previous = updatedNodes[0];LinkedListNode current = updatedNodes[1];System.out.print("\t\tReversed group: ");PrintList.printListWithForwardArrow(previous);// Connect the reversed group to the rest of the linked listSystem.out.println("\n\n\tRe-attaching the reversed group to the rest of the linked list:");LinkedListNode lastNodeOfReversedGroup = ptr.next;lastNodeOfReversedGroup.next = current;ptr.next = previous;ptr = lastNodeOfReversedGroup;System.out.print("\t\t");PrintList.printListWithForwardArrow(dummy.next);break;}}public static void main(String[] args) {int[] inputList = {1, 2, 3, 4, 5, 6, 7, 8};int k = 3;LinkedList inputLinkedList = new LinkedList();inputLinkedList.createLinkedList(inputList);System.out.print("Linked list: ");PrintList.printListWithForwardArrow(inputLinkedList.head);System.out.print("\n");System.out.println();reverseKGroups(inputLinkedList.head, k);}}
Finally, the last step is to repeat the above process for all groups of nodes. This is done by simply not breaking out of the outer loop once the first group has been reversed and attached. After the linked list has been traversed, i.e., ptr
becomes NULL, we return the next node of dummy
, which contains the reversed linked list attached to it.
class Reorder {public static LinkedListNode reverseKGroups(LinkedListNode head, int k) {// Create a dummy node and set its next pointer to the headLinkedListNode dummy = new LinkedListNode(0);dummy.next = head;LinkedListNode ptr = dummy;while (ptr != null) {System.out.println("\tIdentifying a group of " + k + " nodes:");System.out.println("\t\tptr: " + ptr.data);// Keep track of the current positionLinkedListNode tracker = ptr;System.out.print("\t\tCurrent group: ");// Traverse k nodes to check if there are enough nodes to reversefor (int i = 0; i < k; i++) {if (tracker == null) {break;}tracker = tracker.next;if (tracker != null) {System.out.print(tracker.data + " ");} else {System.out.print("");}}// If there are not enough nodes to reverse, break out of the loopif (tracker == null) {System.out.println();System.out.println("\t\tThe above group contains less than " + k + " nodes, so we can't reverse it.\n");System.out.print("\tFinal state of the linked list: ");PrintList.printListWithForwardArrow(dummy.next);break;}System.out.println();System.out.println("\t\tThe above group of " + k + " nodes can be reversed.\n");// Reverse the current group of k nodesSystem.out.println("\tReversing the current group of " + k + " nodes:");LinkedListNode[] updatedNodes = LinkedListReversal.reverseLinkedList(ptr.next, k);LinkedListNode previous = updatedNodes[0];LinkedListNode current = updatedNodes[1];System.out.print("\t\tReversed group: ");PrintList.printListWithForwardArrow(previous);// Connect the reversed group to the rest of the linked listSystem.out.println("\n\n\tRe-attaching the reversed group to the rest of the linked list:");LinkedListNode lastNodeOfReversedGroup = ptr.next;lastNodeOfReversedGroup.next = current;ptr.next = previous;ptr = lastNodeOfReversedGroup;System.out.print("\t\t");PrintList.printListWithForwardArrow(dummy.next);System.out.println("\n\n");}return dummy.next;}// Driver codepublic static void main(String[] args) {int[] inputList = {1, 2, 3, 4, 5, 6, 7, 8};int k = 3;LinkedList inputLinkedList = new LinkedList();inputLinkedList.createLinkedList(inputList);System.out.print("Linked list: ");PrintList.printListWithForwardArrow(inputLinkedList.head);System.out.print("\n");System.out.println();reverseKGroups(inputLinkedList.head, k);}}
Just the code
Here’s the complete solution to this problem:
class ReverseKGroups {public static LinkedListNode reverseKGroups(LinkedListNode head, int k) {LinkedListNode dummy = new LinkedListNode(0);dummy.next = head;LinkedListNode ptr = dummy;while (ptr != null) {LinkedListNode tracker = ptr;for (int i = 0; i < k; i++) {if (tracker == null) {break;}tracker = tracker.next;}if (tracker == null) {break;}LinkedListNode[] updatedNodes = LinkedListReversal.reverseLinkedList(ptr.next, k);LinkedListNode previous = updatedNodes[0];LinkedListNode current = updatedNodes[1];LinkedListNode lastNodeOfReversedGroup = ptr.next;lastNodeOfReversedGroup.next = current;ptr.next = previous;ptr = lastNodeOfReversedGroup;}return dummy.next;}// Driver codepublic static void main(String[] args) {int[][] inputList = {{1, 2, 3, 4, 5, 6, 7, 8},{3, 4, 5, 6, 2, 8, 7, 7},{1, 2, 3, 4, 5},{1, 2, 3, 4, 5, 6, 7},{1}};int[] k = {3, 2, 1, 7, 1};for (int i = 0; i < inputList.length; ++i) {LinkedList inputLinkedList = new LinkedList();inputLinkedList.createLinkedList(inputList[i]);System.out.print((i + 1) + ".\tLinked list: ");PrintList.printListWithForwardArrow(inputLinkedList.head);System.out.println();System.out.print("\n\tReversed linked list: ");LinkedListNode result = reverseKGroups(inputLinkedList.head, k[i]);PrintList.printListWithForwardArrow(result);System.out.println();String hyphens = new String(new char[100]).replace('\0', '-');System.out.println(hyphens);}}}
Solution summary
To recap, the solution to this problem can be divided into the following four main parts:
-
Check if there are nodes present in the current group.
-
If the current group contains nodes, reverse it.
-
Reattach the reversed group to the rest of the linked list.
-
Repeat the process above until there are less than nodes left in the linked list.
Time complexity
The time complexity of this solution is , where is the number of nodes in the list.
Space complexity
The space complexity of this solution is , since we’ll use a constant number of additional variables to maintain the connections between the nodes during reversal.
Level up your interview prep. Join Educative to access 70+ hands-on prep courses.