Solution: Reverse Nodes in k-Group
Let's solve the Reverse Nodes in k-Group problem using the In-Place Reversal of 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.
-
kn -
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 reversal pattern to reduce the space complexity of our solution.
Optimized approach using in-place reversal of a linked list#
The optimized approach is to use less space in memory. We actually need to reverse each group of nodes in place. We can think of each -group of nodes as a separate linked list. For each of these linked lists, applying an in-place linked list reversal solves the original problem. We need to invoke the in-place reversal of linked list code times, where is the size of the linked list.
The slides below illustrate how we would like the algorithm to run:
1 of 31
2 of 31
3 of 31
4 of 31
5 of 31
6 of 31
7 of 31
8 of 31
9 of 31
10 of 31
11 of 31
12 of 31
13 of 31
14 of 31
15 of 31
16 of 31
17 of 31
18 of 31
19 of 31
20 of 31
21 of 31
22 of 31
23 of 31
24 of 31
25 of 31
26 of 31
27 of 31
28 of 31
29 of 31
30 of 31
31 of 31
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 itsnextpointer equal to the head. -
We set a pointer,
ptr, equal to thedummynode. We will use this pointer to traverse the linked list. -
We traverse the linked list till
ptrbecomes 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
trackernodes forward in the linked list. Iftrackerbecomes 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 andtrackerwill point to the node of the current group.
-
-
After the completion of the nested loop, we check if
trackerpoints 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.
-
Note: The
print_list_with_forward_arrowmethod 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
reverse_linked_listfunction in thelinked_list_reversal.pyfile to take an additional parameter,k, which specifies the number of nodes to reverse. -
In the
reverse_k_groupsfunction, for the case wheretrackerdoes not point to NULL, we declare three pointers:-
current -
previous -
next
-
-
We call the
reverse_linked_listfunction, 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
previouspointer now points to the first node of the reversed group while thecurrentandnextpointers 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.pylinked_list_reversal.py
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
ptrpointer is currently pointing to the node immediately before the last node of the reversed group. We initialize a new pointer,last_node_of_reversed_group, 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
currentpointer is currently pointing to the first node of the next group. We set the next node oflast_node_of_reversed_groupto thecurrentpointer. -
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
previousnode is currently pointing to the first node of the reversed group. We set the next node ofptrequal to thepreviouspointer. -
Lastly, we need to set the
ptrpointer 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 theptrpointer equal to thelast_node_of_reversed_grouppointer. -
We break out of the outer loop to end the algorithm once the first reversed group has been reattached to the linked list.
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.
Just the code#
Here's the complete solution to this problem:
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.
Reverse Nodes in k-Group
Stacks: Introduction