6 minute read

What is Linked List?

Linked list is formed with nodes that contains a value and a pointer that points to the next node.

The first node is called head and the last one is called tail. Tail’s pointer will point to null.

What is a pointer?

Reference to something else in memeory. In other words, it works as an address of a value or a data.

Time Complexity of Linked List

  • Access - O(n)
    • this is because it doesn’t have an index. Linked list always starts from the head to the tail to access data.

    traversing through a linked list is slower than an array because the data it stores is scattered in the memory.

  • Search - O(n)
  • Insertion - O(n)
  • Deletion - O(n)

    Similar to hash map, but has a form of an order.

Single vs Double

  • Singly Linked List

    +simple implementation.

    +Lesser memory and less operation.

    -Cannot be iterated.

    less memory, fast insertion and deletion. Less searching, less adding in the front.

  • Doubly Linked List

    +iterated from the front or back.

    +delete previous one is easier.

    -complex to implement

    -require more storage, extra operation when adding and deleting.

    no problem on memory, good operation on searching.

Pros

  • Fast Insertions
  • Fast Deletion
  • Ordered
  • Felxible Size

Cons

  • Slow lookup
  • More memory is needed

Why choose linked list? - Simplicity and ability to grow and shrink.

Where it’s used? - file systems, browser history

example of implementing a singly linked list:

class Node {
    constructor(value) {
        this.value = value;
        this.next = null;
    }
}

class LinkedList {
    constructor(value) {
        this.head = {
            value: value,
            next: null
        }
        this.tail = this.head;
        this.length = 1;
    }

    append(value) {
        const newNode = new Node(value);
        this.tail.next = newNode;  // adding new node next to the last node.
        this.tail = newNode;  // update tail with new node, so next time when append, newer node will be added next to this node.
        this.length++;
    }

    prepend(value) {
        const newNode = new Node(value);
        newNode.next = this.head;
        this.head = newNode;
        this.length++;
    }

    insert(index, value) {
        const newNode = new Node(value);
        
        if (index == 0) {
            this.prepend(value);
            return this;
        }
        else if (index == this.length) {
            this.append(value);
            return this;
        } else if (index < 0) {
            console.log('index out of range. index < 0');
            return false;
        } else if (index > this.length) {
            console.log('index out of range. index > length');
            return false;
        }
        
        let nodeBeforeIndex = this.traverseToIndex(index-1);
        newNode.next = nodeBeforeIndex.next;
        nodeBeforeIndex.next = newNode;
        this.length++;
    }

    remove(index) {
        if (index < 0) {
            console.log('index out of range. index < 0');
            return false;
        } else if (index >= this.length) {
            console.log('index out of range. index > length');
            return false;
        }

        if (index == 0) {
            this.head = this.head.next;
            this.length--;
            return this;
        }
        
        const nodeBeforeIndex = this.traverseToIndex(index-1);
        nodeBeforeIndex.next = nodeBeforeIndex.next.next;
        
        if (index == this.length - 1) this.tail = nodeBeforeIndex;
        this.length--;
        return this;
    }

    traverseToIndex(index) {
        let counter = 0;
        let currentNode = this.head;
        while(counter !== index){
          currentNode = currentNode.next;
          counter++;
        }
        return currentNode;
      }

    show() {
        let node = this.head;
        while (node != null) {
            console.log(node);
            node = node.next;
        }
        console.log('tail: ' + this.tail.value);
        console.log('length: ' + this.length);
    }
}

const myLinkedList = new LinkedList(1);
myLinkedList.append(2);
myLinkedList.append(3);
myLinkedList.insert(3,4);
myLinkedList.remove(3);
myLinkedList.show();

example of implementing a doubly linked list:

class Node {
    constructor(value) {
        this.value = value;
        this.next = null;
        this.prev = null;
    }
}

class DoublyLinkedList {
    constructor(value) {
        this.head = {
            value: value,
            next: null,
            prev: null
        };
        this.tail = this.head;
        this.length = 1;
    }

    append(value) {
    const newNode = new Node(value);
    newNode.prev = this.tail
    this.tail.next = newNode;
    this.tail = newNode;
    this.length++;
    }

    prepend(value) {
        const newNode = new Node(value);
        this.head.prev = newNode
        this.head = newNode;
        this.length++;
    }

    insert(index, value){
        const newNode = new Node(value);

        if (index == 0) {
            this.prepend(value);
            return this;
        }
        else if (index == this.length) {
            this.append(value);
            return this;
        } else if (index < 0) {
            console.log('index out of range. index < 0');
            return false;
        } else if (index > this.length) {
            console.log('index out of range. index > length');
            return false;
        }
    
        const leader = this.traverseToIndex(index-1);
        const follower = leader.next;
        leader.next = newNode;
        newNode.prev = leader;
        newNode.next = follower;
        follower.prev = newNode;
        this.length++;
    }

    traverseToIndex(index) {
      //Check parameters
      let counter = 0;
      let currentNode = this.head;
      while(counter !== index){
        currentNode = currentNode.next;
        counter++;
      }
      return currentNode;
    }

    show() {
        let node = this.head;
        while (node != null) {
            console.log(node);
            node = node.next;
        }
        console.log('tail: ' + this.tail.value);
        console.log('length: ' + this.length);
    }
  }