Reverse Nodes in K-Group
Given the HEAD
of a linked list, reverse the nodes of the list K
at a time, and return the modified list.
Imagine K
like a group/window or sublist of the whole list. K
is a positive integer which 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. Meaning if K=2
and the number of nodes in the Linked List is 3
then the first 2 nodes will be reversed and last one will be left as it is.
For example: In the given Linked List K=2
, So the First 2 groups (i.e. first 4 nodes) are reversed but Last node remains as it is, because length of this group is less than K
.
Intuition
Reversing a Linked List means the HEAD
node will be TAIL
and the TAIL
node will be the HEAD
of the Linked List. Checkout Reverse a Linked List if you've not solved how to reverse a Linked List yet. After reversing the Linked List will look like this bellow.
According to the Diagram, after reversal,
- Kth Node Will be the New HEAD (old tail:
[2]
) of the Group - The New Tail (old head:
[1]
) must be connect to the next group's head.
To do so, we need to find theKth Node
first.
Finding the Kth Node
To find the Kth Node
, let's assume there was a K Group
of Linked List already reversed before the actual head, like this
The DUMMY
node is the TAIL
of the previous K Group
. Let's consider the DUMMY
as the TAIL
node of the previously reversed imaginary K Group
# 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: Optional[ListNode], k: int) -> Optional[ListNode]:
dummy = ListNode(next=head)
prev_tail = dummy
kth_node = self.getKthNode(prev_tail, k)
Ok so how can we find the Kth Node
? Let's visualize the process first, then we will write the code.
The pointer
is pointing at the DUMMY HEAD
. From here, the pointer
needs K
number of iteration to find the Kth Node
Implement a helper function to find the Kth Node.
def getKthNode(self, pointer: Optional[ListNode], k: int) -> Optional[ListNode]:
while pointer and k > 0:
pointer = pointer.next
k = k - 1 # decrementing K
return pointer # this is the Kth node of a group
Now we've got the Kth Node
and the Linked List is looking like this bellow,
The prev_tail
is at the DUMMY HEAD
as it was but we have a new pointer kth_node
is pointing at the Tail of the Kth Group.
Now reverse the 1st K group of nodes from [1]->[2] to [2]->[1]
.
Keep one thing in mind that we will stop reversing when Kth node
is NULL
. The Kth Node
can only be NULL
when the number of nodes in a K Group is less than K
.
while True:
kth_node = self.getKthNode(prev_tail)
if not kth_node:
return
This is our base condition.
Reverse K Group of Nodes
In this section we will reverse a K group of nodes, but before further do, let's take a look at the high level workflow.
To reverse a Linked List, the current node need to be connected to it's previous node instead of it's next node. So must need a curr
and prev
pointer to set curr.next=prev
Checkout Reverse a Linked List if you have not done it yet.
Let's take our curr
pointer pointing at the HEAD
and prev
pointer at the Head of the next K Group. Because after reversing, Head of the current K Group will be the Tail and this Tail need to be connected to it's next Group (with the head of the next K Group), basically need to achieve this formation [DUMMY]->[2]->[1]->[3]...
As you can see, node [1] is connected to [3] (head of the next group). We will keep reversing until curr
reaches to the Head of the next group which is next_group_head
.
The curr
pointer has reached to next_group_head
, so we have reversed all nodes in the current K group. Let's implement this part of the code
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
dummy = ListNode(next=head)
prev_tail = dummy
while True:
kth_node = self.getKthNode(prev_tail)
if not kth_node:
break
curr = prev_tail.next
next_group_head = kth_node.next
# Tricky part is initalizing the prev pointer.
# we know the current Head node will be the Tail node
# after reversing the nodes in current K Group
# So need to link the current Head (i.e. reversed Tail eventually)
# with the Head of the next K Group to maintain the continuety
prev = next_group_head
# reverse nodes in currnt K group
while curr != next_group_head:
# this part the code is actually trivial.
# taken from reverse linked list problem
curr_next = curr.next
curr.next = prev # establishing link with the prev node
prev = curr # moving prev forward
curr = curr_next # moving curr forward
# all nodes in the K group has reversed. what's next?
Reconcile Head of the K Group
Next part is getting ready for reversing the Next K Group. To do so, we need to shift the Tail of the Previous K Group (i.e. prev_tail
pointer).
But Look like the prev_tail
is still connected to the old head [1]
which the new tail of the K group. This prev_tail
needs to be linked with the new head [2]
first.
Since prev_tail.next
is going to be modified to connect with the new reversed head [2]
and also need to move prev_tail
to the new reversed tail [1]
we have to save the Link from prev-tail
to old_head
in a temporary variable next_prev_tail
like
next_prev_tail = prev_tail.next
Now connect prev_tail
with the new reversed head [2]
(reconciling the dummy head)
prev_tail.next = kth_node
Finally move the prev-tail
pointer to next_prev_tail
prev_tail = next_prev_tail
Now again find the Kth node
for the Next K group. We will keep finding Kth node
and Reversing
nodes in the K group until the Kth Node is NULL. I'm gonna skip showing diagram for 2nd K Group which will be reversed same as the 1st K group.
2nd K Group also got reversed. Find another Kth Node from the prev_tail
position.
Looks like we have reached at the end, Kth node is NULL means there is no K Sized Group left to be reversed. So, We will return the reversed head of the Linked List which is dummy.next
.
Let's write the whole code base altogether.
Complete Code Snippet
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
dummy = ListNode(next=head)
prev_tail = dummy
while True:
kth_node = self.getKthNode(prev_tail, k)
if not kth_node:
break
curr = prev_tail.next
next_group_head = kth_node.next
# Tricky part is initalizing the prev pointer.
# we know the current Head node will be the Tail node
# after reversing the nodes in current K Group
# So need to link the current Head (i.e. reversed Tail eventually)
# with the Head of the next K Group to maintain the continuety
prev = next_group_head
# reverse the nodes in K group
while curr != next_group_head:
curr_next = curr.next
curr.next = prev
prev = curr
curr = curr_next
# all nodes in the current K group is reversed
# link the prev_tail with the new reversed head
next_prev_tail = prev_tail.next
# Kth node initially was the tail which became the reversed HEAD
prev_tail.next = kth_node
# moving prev_tail to find the next kth node
prev_tail = prev_tail_next
# all of the K groups have been reversed.
return dummy.next
Complexity Analysis
Time Complexity: O(N^K) where K is the number of groups. But we can drop constant for complexity analysis. So we can say the Time complexity is O(N)
Space Complexity: Constant, No extra space taken
Thanks for Stopping by! Happy Leet coding.
Connect with me on