Mukhtar Bahadory

Remove Linked List Elements

Contents

Prompt

Remove all elements from a linked list of integers that have value val.

Input

1->2->6->3->4->5->6, val = 6

Output

1->2->3->4->5

Conceptual Overview

This is a really neat problem and there are two very interesting solutions to it. In this post, we're going to be looking at the iterative solution as it is most optimal, but I do think the recursive one is signficantly more intuitive.

You might approach this problem with one pointer where you simply iterate through and if you meet a bad value, delete it in place. However, you will quickly realize that the problem isn't quite that easy as the head can also be deleted.

You will idealy need three additional pointers. One pointer is going to be a container helper node, containing the head as it's next value. This is a node itself because that it either stores the head itself, or something that uses it as a pointer can discover the head for it and reflect that information over via referential equality.

The second pointer will be set equal to the helper node and it serves to re-assign it's .next head value to the first good node discovered. A good node is any node that doesn't need to be deleted.

The third pointer serves as the discovery iterator and also the iterative condition; we will continue to iterate for as long as it's not null.

Let's look at some examples.

Examples

sentinel = Dicussed Pointer 1
prev = Discussed Pointer 2
curr = Dicussed Pointer 3

Input 1

1->2->6->3->4->5->6, val = 6

sentinel = x->1->2->6->3->4->5->6
prev = x->1->2->6->3->4->5->6
curr = 1->2->6->3->4->5->6

Output 1

1->2->3->4->5

What's firstly important to note here is that head === sentinel.next === prev.next === curr.

In this example, sentinel.next is already a good node and also the head; in this case prev will simply serve to easily skip over bad nodes discovered by curr. Skipes made on prev will reflect over onto sentinel.next even though prev is being reassigned to curr incase of good nodes. This is because of the previously stated referential equality.

Input 2

6->6->6->3->4->5->6, val = 6

sentinel = x->6->6->6->3->4->5->6
prev = x->6->6->6->3->4->5->6
curr = 6->6->6->3->4->5->6

Output 2

3->4->5

In this example, since sentinel and prev are initially the same in memory object, as curr explores bad nodes, including the first one, prev.next will then point to curr.next in hopes that it will be a good node and it can find a node and reflect that over to sentinel and then on the next iteration, it will stop serving as a head pointer as it will then point to whatever curr would be.

Space and Time Complexity

Space: O(1)
The only additional variables created are pointer types.

Time: O(n)
The input linked list is only iterated over once.

Solution

function removeElements(head, val) {
  const sentinel = new ListNode(0)
  sentinel.next = head

  let prev = sentinel
  let curr = head

  while (curr) {
    if (curr.val === val) {
      prev.next = curr.next
    } else {
      prev = curr
    }

    curr = curr.next

  }

  return sentinel.next
};