19. Remove Nth Node From End of List#
Difficulty: Medium
Link to Problem: To see the Remove Nth Node From End of List problem on LeetCode, click here!
Given the head of a linked list, remove the \(n^{th}\) node from the end of the list and return its head.
Constraints:
The number of nodes in the list is
sz
.1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
Follow up: Could you do this in one pass?
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def removeNthFromEnd(head, n):
# Create a dummy node to handle the case of removing the head node.
dummy = ListNode(0)
dummy.next = head
first = dummy
second = dummy
# Advance the first pointer by n+1 nodes.
for _ in range(n + 1):
first = first.next
# Move both pointers simultaneously until first reaches the end.
while first is not None:
first = first.next
second = second.next
# Remove the nth node by updating the next pointer of the previous node.
second.next = second.next.next
return dummy.next # Return the modified head of the linked list
Explanation:#
We start by defining a
ListNode
class to represent individual nodes in a linked list. Each node has two attributes:val
(the value of the node) andnext
(a reference to the next node in the list).The
removeNthFromEnd
function takes the head of a linked list (head
) and the valuen
, which represents the position from the end of the list of the node to be removed. It returns the modified head of the linked list after the removal.We create a dummy node (
dummy
) at the beginning of the list. This dummy node simplifies the code by handling cases where the head of the list needs to be removed.We initialize two pointers,
first
andsecond
, both initially pointing to the dummy node.To advance the
first
pointer byn+1
nodes, we use afor
loop. This positions thefirst
pointern
nodes ahead of thesecond
pointer.We then move both
first
andsecond
pointers simultaneously untilfirst
reaches the end of the list. This ensures that thesecond
pointer ends up pointing to the node that needs to be removed.To remove the nth node from the end, we update the
next
pointer of the node pointed to by thesecond
pointer to skip over the node to be removed.Finally, we return
dummy.next
, which is the modified head of the linked list without the removed node.Additionally, there are two helper functions:
createLinkedList(values)
creates a linked list from a list of values.convertToList(head)
converts a linked list back into a list of values.
# Helper function to create a linked list from a list of values.
def createLinkedList(values):
if not values:
return None
head = ListNode(values[0])
current = head
for val in values[1:]:
current.next = ListNode(val)
current = current.next
return head
# Helper function to convert a linked list to a list of values.
def convertToList(head):
result = []
current = head
while current:
result.append(current.val)
current = current.next
return result
Let’s explain the two helper functions:
createLinkedList(values)
: This function creates a linked list from a list of values.Input:
values
is a list containing values that you want to insert into the linked list.Output: The function returns the head of the created linked list.
Explanation:
The function starts by checking if the
values
list is empty. If it is, it returnsNone
to indicate an empty linked list.If the
values
list is not empty, it creates the first node of the linked list with the value from the first element ofvalues
.It then iterates through the remaining elements of
values
and creates new nodes for each value, connecting them together using thenext
attribute to form a linked list.Finally, it returns the head of the linked list, which is the first node created.
convertToList(head)
: This function converts a linked list into a list of values.Input:
head
is the head node of the linked list that you want to convert.Output: The function returns a list containing the values of the nodes in the linked list in order.
Explanation:
The function starts by initializing an empty list,
result
, to store the values of the linked list nodes.It then iterates through the linked list starting from the
head
node and appends theval
attribute of each node to theresult
list.The iteration continues until it reaches the end of the linked list (i.e., the
next
attribute of the current node becomesNone
).After the iteration is complete, the function returns the
result
list, which now contains the values of all the nodes in the linked list in the same order as they appear in the linked list.
These helper functions are useful for creating linked lists from lists of values and converting linked lists back into lists of values, making it easier to work with linked list examples and test cases in a more familiar list format.
Test cases#
# Example 1
head1 = createLinkedList([1, 2, 3, 4, 5])
n1 = 2
new_head1 = removeNthFromEnd(head1, n1)
print(convertToList(new_head1))
[1, 2, 3, 5]
# Example 2
head2 = createLinkedList([1])
n2 = 1
new_head2 = removeNthFromEnd(head2, n2)
print(convertToList(new_head2))
[]
# Example 3
head3 = createLinkedList([1, 2])
n3 = 1
new_head3 = removeNthFromEnd(head3, n3)
print(convertToList(new_head3))
[1]
Certainly! Let’s analyze the time and space complexity of the removeNthFromEnd
function in isolation:
Time Complexity:
The
removeNthFromEnd
function performs two passes through the linked list. In the worst case, it needs to traverse the entire linked list of length \(N\).The first pass positions the
first
pointer \(n\) nodes ahead of thesecond
pointer. This pass takes \(O(N)\) time because it involves iterating through all N nodes.The second pass moves both
first
andsecond
pointers simultaneously untilfirst
reaches the end of the list. This also takes \(O(N)\) time in the worst case.Therefore, the overall time complexity of the
removeNthFromEnd
function is \(O(N)\).
Space Complexity:
The
removeNthFromEnd
function uses a constant amount of extra space for its variables (e.g.,first
,second
, anddummy
). This space usage does not depend on the size of the linked list.The space complexity is \(O(1)\), indicating that the space used by the function is constant and independent of the size of the input linked list.
In summary, the removeNthFromEnd
function has a time complexity of \(O(N)\) and a space complexity of \(O(1)\). It efficiently removes the nth node from the end of the linked list with a single pass through the list and constant extra space usage.