Design Linked List

MediumDesign · Linked List · Sentinel Nodes🧩 Linked List TechniquesLeetCode #707 ↗Updated

Problem

Implement your own list class MyLinkedList (0-indexed) without built-in list types. get(i) returns the value at index i or -1 if invalid; addAtHead(v) / addAtTail(v) insert at the ends; addAtIndex(i, v) inserts before index i (appends if i == size, ignored if i > size); deleteAtIndex(i) removes index i if it exists.
InputaddAtHead(1) addAtTail(3) addAtIndex(1,2)Outputlist = 1 → 2 → 3
Inputget(1)Output2
InputdeleteAtIndex(1) get(1)Output3 (list = 1 → 3)

0 ≤ index, val ≤ 1000  ·  at most 2000 calls  ·  do not use a built-in linked list

Try it yourself

Java
class MyLinkedList {
    public MyLinkedList() {
        
    }

    public int get(int index) {
        
    }

    public void addAtHead(int val) {
        
    }

    public void addAtTail(int val) {
        
    }

    public void addAtIndex(int index, int val) {
        
    }

    public void deleteAtIndex(int index) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Sentinel (dummy) nodes remove every special case for the head and tail: each insert or delete becomes “find the neighbour, relink two pointers”. With a doubly linked list plus a size counter you can also walk from whichever end is closer.
PatternLinked List Techniques

Instinct Nodes joined by next pointers and the task is to reverse, splice, merge, reorder or remove in place - grab a dummy head and rewire pointers carefully.

Memory hack Save next, flip, step - never let go of a rope before you've grabbed the next one.

Open the Linked List Techniques guide →

Approaches

Brute ForceGrowable Array (Not Really a List)

Time get O(1), add/delete O(n)Space O(n)

Store values in an int[] that doubles when full. Inserting or deleting at index i shifts everything after it by one slot with System.arraycopy. Index checks are the same as in a real list.

Java
class MyLinkedList {
    private int[] data = new int[8];
    private int size = 0;
    public MyLinkedList() {}
    public int get(int index) {
        return index >= 0 && index < size ? data[index] : -1;
    }
    public void addAtHead(int val) { addAtIndex(0, val); }
    public void addAtTail(int val) { addAtIndex(size, val); }
    public void addAtIndex(int index, int val) {
        if (index < 0 || index > size) return;
        if (size == data.length) data = Arrays.copyOf(data, size * 2);
        System.arraycopy(data, index, data, index + 1, size - index); // shift right
        data[index] = val;
        size++;
    }
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        System.arraycopy(data, index + 1, data, index, size - index - 1); // shift left
        size--;
    }
}

Fast random access, but addAtHead is O(n) and it dodges the point of the exercise.

At a glance

LevelApproachTimeSpace
Brute ForceGrowable Array (Not Really a List)get O(1), add/delete O(n)O(n)
BetterSingly Linked List + Dummy HeadO(index) per op, addAtTail O(n)O(n)
OptimalDoubly Linked List + Two SentinelsO(min(i, n−i)), head/tail O(1)O(n)
Common pitfalls
  • Treating addAtIndex(size, v) as invalid — it must append.
  • Forgetting to update size on one of the paths, which breaks every later bounds check.
  • Without a dummy head, inserting or deleting at index 0 needs separate code that is easy to get wrong.
Interview tips
  • Ask singly vs doubly first; then choose sentinels to remove edge cases.
  • Write a single “find node / predecessor” helper and reuse it everywhere.
  • Test index 0, index == size, and index > size for each method.
Pattern
Sentinel nodes + size counter → implement list operations without head/tail special cases.
My notes saved in this browser