Skip to content

Implementing a Linked List

When storing multiple elements, we typically use arrays. But arrays have a drawback: slow insertion and deletion. Since arrays use contiguous memory — which enables random access with O(1) read efficiency — insertions and deletions are complex, with an average time complexity of O(n).

Linked lists are different: elements in a linked list are not stored contiguously in memory. Each element consists of a node that stores the element itself and a reference (pointer) to the next element. As shown in the diagram:

linked-list

Let's implement it in code:

javascript
function ListNode(val, next) {
  this.val = (val===undefined ? 0 : val)
  this.next = (next===undefined ? null : next)
}

Linked List Algorithm Problems

Now that we've implemented the linked list data structure, let's solve a few linked list problems:

Remove Duplicates from Sorted List

Given the head of a sorted linked list, delete all duplicates such that each element appears only once. Return the linked list sorted as well.

Input: head = [1,1,2]
Output: [1,2]

This is considered easy on LeetCode. If you can understand the problem, you can solve it:

javascript
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var deleteDuplicates = function(head) {
    if (!head) {
        return head;
    }
    let cur = head;
    // Iterate through the next element of the current node
    while (cur.next) {
      // If the next element has the same value as current, skip it by pointing to the one after
        if (cur.val === cur.next.val) {
            cur.next = cur.next.next;
        } else {
            cur = cur.next;
        }
    }
    return head;
 };
bash
Time complexity: O(n)
Space complexity: O(1)

Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new sorted list. The new list should be made by splicing together the nodes of the first two lists.

Input: l1 = [1,2,4], l2 = [1,3,4]
Output: [1,1,2,3,4,4]

There are two approaches to this problem. Let's start with the recursive solution:

javascript
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var mergeTwoLists = function(l1, l2) {
    if(l1 === null){  // If l1 is null, return l2
        return l2
    }else if(l2 === null){    // If l2 is null, return l1
        return l1
    }else if(l1.val < l2.val){    // If l1 < l2, recurse with l1.next and l2
        l1.next = mergeTwoLists(l1.next, l2);
        return l1
    }else{
        // If l2 <= l1, recurse with l1 and l2.next
        l2.next = mergeTwoLists(l1, l2.next);
        return l2
    }
};

The recursive version is fairly easy to understand — most people can follow the code. The second approach is slightly harder to grasp. It uses iteration with a sentinel node (prehead), which makes it easier to return the merged list at the end.

javascript
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var mergeTwoLists = function(l1, l2) {
  // Sentinel node
  const prehead = new ListNode(-1);

  let prev = prehead;
  // Iterate
  while (l1 != null && l2 != null) {
      if (l1.val <= l2.val) {
          prev.next = l1;
          l1 = l1.next;
      } else {
          prev.next = l2;
          l2 = l2.next;
      }
      prev = prev.next;
  }

  // At most one of l1 and l2 is non-null at this point, so connect it to the end
  prev.next = l1 === null ? l2 : l1;

  return prehead.next;
};
bash
Time complexity: O(m + n), where m and n are the lengths of the two lists.
Space complexity: O(1).

Remove Nth Node From End of List

This problem is rated Medium on LeetCode:

bash
Given the head of a linked list, remove the nth node from the end of the list and return its head.

Follow up: Could you do this in one pass?

Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]

Algorithm code:

javascript
var removeNthFromEnd = function(head, n) {

  // Helper function to get linked list length
  function getLength(head){
      let length = 0;
      while(head){
          length++;
          head = head.next;
      }
      return length;
  }

  // Create a dummy node whose next is head
  const dummy = new ListNode(0, head);

  const length = getLength(head);

  // Traverse (length - n) times to reach the node before the one to delete
  let current = dummy;
  for(let i = 0; i < length - n ; i++){
      current =  current.next;
  }
  // Delete the node by skipping it
  current.next = current.next.next;
  return dummy.next;
};

This might be a bit hard to follow. LeetCode's official explanation is very clear. Here's the diagram:

p1

dummy is current, and length - n equals 2, so current is the node with value 2. Simply change the next pointer to skip the target node.

bash
Time complexity: O(n), where n is the length of the list.
Space complexity: O(1).