The Problem
Given the head of a linked list, remove the nth node from the end of the list and return its head.
Examples
Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]
Input: head = [1], n = 1
Output: []
Input: head = [1,2], n = 1
Output: [1]
Constraints
- The number of nodes in the list is
sz. - 1 <= sz <= 30
- 0 <= Node.val <= 100
- 1 <= n <= sz
Brute Force Solution
func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
var nodes: [ListNode] = []
var curr = head
while curr != nil {
nodes.append(curr!)
curr = curr!.next
}
let removeIndex = nodes.count - n
if removeIndex == 0 {
return head?.next
}
nodes[removeIndex - 1].next = nodes[removeIndex].next
return head
}
Explanation
One way to solve this problem is to use extra memory by iterating over all elements in head and storing them in an array. The brute force solution allows us to know the index of each element and delete the node.
Time/Space Complexity
- Time complexity: O(n)
- Space complexity: O(n)
Recursive Solution
func rec(_ head: ListNode?, _ n: inout Int) -> ListNode? {
guard let head = head else {
return nil
}
head.next = rec(head.next, &n)
n -= 1
if n == 0 {
return head.next
}
return head
}
func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
var n = n
return rec(head, &n)
}
Explanation
If you take a closer look, the recursive solution is no different from the brute force solution and uses the same time and space complexity.
Time/Space Complexity
- Time complexity: O(n)
- Space complexity: O(n)
Two Pointers Solution
func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {
let dummyNode = ListNode(0, head)
var n = n
var curr = head
var l: ListNode? = dummyNode
var r: ListNode? = head
while n > 0 && r != nil {
r = r?.next
n -= 1
}
while r != nil {
l = l?.next
r = r?.next
}
l?.next = l?.next?.next
return dummyNode.next
}
Explanation
We can solve this problem in a more memory-efficient way by using the two-pointers technique. We create an offset between the left and right pointers by looking at the nth node. The right pointer is shifted by n, while the left pointer starts from 0 and moves by 1. When the right pointer becomes nil, the left pointer will point to the node we need to delete.
For example, for the input [1, 2, 3, 4, 5] and n = 2, at the start, the left pointer will be at 1, and the right pointer will be at 3. We keep shifting pointers until the right pointer reaches the end of the list. At this point, the left pointer will be at 4, and the right pointer will be nil.
Time/Space Complexity
- Time complexity: O(n)
- Space complexity: O(1)