Link Search Menu Expand Document (external link)

Linked Lists & Double Linked Lists

A linked list is just series of nodes, linked together, forming a list. Each node has two parts - the data and a pointer, which points to the next node. The very first element of the list is called “Head” and the very last is called “Tail”.


Double Linked Lists are same as Linked Lists with the only difference, that each Node keeps references to the next node and to the previous node. This helps you optimize part of the operations.

When we manipulate the elements (adding, removing, switching places), we actually manipulate the links (the pointers).

Pros:

  • Super fast Inserts/ Deletes (at front) - constant time O(1)
  • No fixed capacity, can grow incrementally

Cons:

  • No indexes => no random access (access by index) to the elements
  • Access (Get/ Set) is linear O(N), because we have to iterate all the elements, starting from the head

Stacks & Queues are internally implemented with Linked Lists.

There are also circular Linked Lists. They do not have head and tail.

Summary

FIND/ DELETE BY VALUE ADD/REMOVE (FRONT) ADD/REMOVE (BACK)
O(N) O(1) O(N)
linear constant linear
not that fast ultra fast not that fast

Discussion

Linked List vs Double Linked List

Double Linked Lists are same as Linked Lists with the only difference, that each Node keeps references to the next node and to the previous node. This helps you optimize part of the operations.

Array vs ArrayList vs Linked List

  • Array and Linked List are the basic Data Structures, while ArrayList is the API, build on top of the arrays, with some additional functionality.

  • Arrays can contain primitives, while ArrayLists & Linked Lists contain only objects.

  • Arrays have fixed size, ArrayList have fixed capacity, that can grow dynamically and the size of the LinkedLists grow incrementally with the elements

  • Arrays & ArrayLists offer random access to elements (by index), whereas the access takes constant time O(1). LinkedLists don’t and the access takes linear time O(N)

  • Arrays & ArrayLists take linear time O(N) to insert/ delete element, because we have to shift all elements right or left. Inserts & Deletes in front of the LinkedLists takes constant time O(1).

  • Array List takes more memory most of the time, because i.e. if the capacity is 2000, but we may have 1001 elements.

o Array ArrayList Linked List
FIXED SIZE YES NO (grows dynamically) NO (grows incrementally)
ALLOCATED MEMORY exact more (because of the capacity) exact
RANDOM ACCESS (BY INDEX) YES YES NO
GET/SET O(1) O(1) FRONT: O(1) / INNER & BACK: O(N)
INSERT/DELETE O(N) O(N) FRONT: O(1) / INNER & BACK: O(N)
CONTAIN PRIMITIVES YES NO NO

Simple Implementation of Linked List

public class LinkedList<T> {

    private static class Node<T> {
        private T data;
        private Node next;

        public Node(T data) {
            this.data = data;
        }
    }

    private Node head;
    private int size = 0;

    public void addFront(T o) {
        Node node = new Node(o);
        if (this.head != null) {
            node.next = this.head;
        }
        this.head = node;
        this.size++;
    }

    public void removeFront() {
        if (this.head == null) {
            return;
        }

        this.head = this.head.next;
        this.size--;
    }

    public void addBack(T o) {
        Node node = new Node(o);

        Node last = getLastNode();
        if (last == null) {
            this.head = node;
        } else {
            last.next = node;
        }

        this.size++;
    }

    public void removeBack() {
        if (this.head == null) {
            return;
        }

        if (this.head.next == null) {
            this.head = null;
            return;
        }

        Node curr = this.head;
        while (curr.next.next != null) {
            curr = curr.next;
        }
        curr.next = null;

        this.size--;
    }

    public T getFirst() {
        if (this.head == null) {
            return null;
        }
        return (T) this.head.data;
    }

    public T getLast() {
        Node last = getLastNode();
        return last == null ? null : (T) last.data;
    }

    public void clear() {
        this.head = null;
        this.size = 0;
    }

    public int size() {
        return this.size;
    }

    public boolean deleteValue(T o) {
        if (this.head == null || o == null) {
            return false;
        }

        if (this.head.data.equals(o)) {
            if (this.head.next != null) {
                this.head = this.head.next;
            } else {
                this.head = null;
            }
            this.size--;
            return true;
        }

        Node curr = this.head;
        while (curr.next != null && !curr.next.data.equals(o)) {
            curr = curr.next;
        }
        if (curr.next.data.equals(o)) {
            bypassNext(curr);

            this.size--;
            return true;
        }
        return false;
    }

    private void bypassNext(Node node) {
        if (node.next == null) {
            return;
        }

        if (node.next.next != null) {
            node.next = node.next.next;
        } else {
            node.next = null;
        }
    }

    public boolean contains(T o) {
        return findByValue(o) != null;
    }

    private Node findByValue(T o) {
        if (this.head == null || o == null) {
            return null;
        }
        Node curr = this.head;
        while (curr != null) {
            if (curr.data.equals(o)) {
                return curr;
            }
            curr = curr.next;
        }
        return null;
    }

    private Node getLastNode() {
        if (this.head == null) {
            return null;
        }

        Node curr = this.head;
        while (curr.next != null) {
            curr = curr.next;
        }
        return curr;
    }
}

Simple Implementation of Double Linked List

public class DoubleLinkedList<T> {

    private static class Node<T> {
        private T data;

        private Node prev;
        private Node next;

        public Node(T data) {
            this.data = data;
        }
    }

    private Node head;
    private int size = 0;

    public void addFront(T o) {
        Node node = new Node(o);
        if (this.head != null) {
            node.next = this.head;
            node.next.prev = node;
        }
        this.head = node;
        this.size++;
    }

    public void removeFront() {
        if (this.head == null) {
            return;
        }

        this.head = this.head.next;
        this.head.prev = null;
        this.size--;
    }

    public void addBack(T o) {
        Node node = new Node(o);

        Node last = getLastNode();
        if (last == null) {
            this.head = node;
        } else {
            last.next = node;
            node.prev = last;
        }

        this.size++;
    }

    public void removeBack() {
        Node last = getLastNode();
        if (last != null) {
            last.prev = null;
            this.size--;
        }
    }

    public T getFirst() {
        if (this.head == null) {
            return null;
        }
        return (T) this.head.data;
    }

    public T getLast() {
        Node last = getLastNode();
        return last == null ? null : (T) last.data;
    }

    public void clear() {
        this.head = null;
        this.size = 0;
    }

    public int size() {
        return this.size;
    }

    public boolean deleteValue(T o) {
        if (this.head == null || o == null) {
            return false;
        }

        Node node = findByValue(o);
        if (node != null) {
            if (node.next != null) {
                node.prev.next = node.next;
                node.next.prev = node.prev;
            } else {
                node.prev.next = null;
            }
            this.size--;
            return true;
        }

        return false;
    }

    public boolean contains(T o) {
        return findByValue(o) != null;
    }

    private Node findByValue(T o) {
        if (this.head == null || o == null) {
            return null;
        }
        Node curr = this.head;
        while (curr != null) {
            if (curr.data.equals(o)) {
                return curr;
            }
            curr = curr.next;
        }
        return null;
    }

    private Node getLastNode() {
        if (this.head == null) {
            return null;
        }

        Node curr = this.head;
        while (curr.next != null) {
            curr = curr.next;
        }
        return curr;
    }
}