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)