Skip to content

Design Linked list⚓︎

Link

Description⚓︎

Design your implementation of the linked list. You can choose to use a singly or doubly linked list.

A node in a singly linked list should have two attributes: val and next. val is the value of the current node, and next is a pointer/reference to the next node. If you want to use the doubly linked list, you will need one more attribute prev to indicate the previous node in the linked list. Assume all nodes in the linked list are 0-indexed.

Implement the MyLinkedList class:

  • MyLinkedList() Initializes the MyLinkedList object.
  • int get(int index) Get the value of the index-th node in the linked list. If the index is invalid, return -1.
  • void addAtHead(int val) Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
  • void addAtTail(int val) Append a node of value val as the last element of the linked list.
  • void addAtIndex(int index, int val) Add a node of value val before the index-th node in the linked list. If index equals the length of the linked list, the node will be appended to the end of the linked list. If index is greater than the length, the node will not be inserted.
  • void deleteAtIndex(int index) Delete the index-th node in the linked list, if the index is valid.

Example 1:

  • Input:
  • ["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
  • [[], [1], [3], [1, 2], [1], [1], [1]]
  • Output:
  • [null, null, null, null, 2, null, 3]

Explanation:

1
2
3
4
5
6
7
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // linked list becomes 1->2->3
myLinkedList.get(1);              // return 2
myLinkedList.deleteAtIndex(1);    // now the linked list is 1->3
myLinkedList.get(1);              // return 3

Constraints:

  • 0 <= index, val <= 1000
  • Please do not use the built-in LinkedList library.
  • At most 2000 calls will be made to get, addAtHead, addAtTail, addAtIndex and deleteAtIndex.

Solution⚓︎

With Dummy Head⚓︎

class MyLinkedList {
public:
    struct LinkedNode {
        int val;
        LinkedNode *next;
        LinkedNode(int val) : val(val), next(nullptr) {}
    };
    MyLinkedList() {
        _dummyHead = new LinkedNode(0);
        _size = 0;
    }

    int get(int index) {
        if (index >= _size || index < 0) return -1;
        LinkedNode *curr = _dummyHead->next;
        while (index-- && curr) curr = curr->next;
        if (curr) return curr->val;
        return -1;
    }

    void addAtHead(int val) {
        LinkedNode *newNode = new LinkedNode(val);
        newNode->next = _dummyHead->next;
        _dummyHead->next = newNode;
        _size++;
    }

    void addAtTail(int val) {
        LinkedNode *newNode = new LinkedNode(val);
        LinkedNode *curr = _dummyHead;
        while (curr->next != nullptr) curr = curr->next;
        curr->next = newNode;
        _size++;
    }

    void addAtIndex(int index, int val) {
        if (index > _size) return;
        if (index < 0) index = 0;
        LinkedNode *newNode = new LinkedNode(val);
        LinkedNode *curr = _dummyHead;
        while (index-- && curr->next) curr = curr->next;
        newNode->next = curr->next;
        curr->next = newNode;
        _size++;
    }

    void deleteAtIndex(int index) {
        if (index >= _size || index < 0) return;
        LinkedNode *curr = _dummyHead;
        while (index-- && curr->next) curr = curr->next;
        LinkedNode *temp = curr->next;
        curr->next = curr->next->next;
        delete temp;
        temp = nullptr;
        _size--;
    }
private:
    int _size;
    LinkedNode *_dummyHead;
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */
  • Time complexity: \(O(index)\) for operations involving index, \(O(1)\) for the rest;
  • Space complexity: \(O(n)\).

Note: When we insert with index < 0, test case will just treat as insert at index = 0.

Code with comments:

class MyLinkedList {
public:
    // Node structure representing each element in the list
    struct LinkedNode {
        int val;           // Value of the node
        LinkedNode *next;  // Pointer to the next node in the list
        LinkedNode(int val) : val(val), next(nullptr) {}
    };

    // Constructor
    MyLinkedList() {
        _dummyHead = new LinkedNode(0);  // Create a dummy head to simplify operations
        _size = 0;                       // Initialize the size of the list as 0
    }

    // Get the value of the index-th node in the linked list
    int get(int index) {
        if (index >= _size || index < 0) return -1; // Return -1 for invalid index
        LinkedNode *curr = _dummyHead->next;        // Start from the first real node
        while (index--) curr = curr->next;          // Move to the correct node
        return curr->val;                           // Return the value of the node
    }

    // Add a node of value val before the first element of the linked list
    void addAtHead(int val) {
        LinkedNode *newNode = new LinkedNode(val);  // Create a new node
        newNode->next = _dummyHead->next;           // Point the new node to the first real node
        _dummyHead->next = newNode;                 // Insert the new node after dummy head
        _size++;                                    // Increase the size of the list
    }

    // Append a node of value val to the last element of the linked list
    void addAtTail(int val) {
        LinkedNode *newNode = new LinkedNode(val);  // Create a new node
        LinkedNode *curr = _dummyHead;              // Start from the dummy head
        while (curr->next != nullptr)               // Traverse to the end of the list
            curr = curr->next;
        curr->next = newNode;                       // Append the new node
        _size++;                                    // Increase the size of the list
    }

    // Add a node of value val before the index-th node in the linked list
    void addAtIndex(int index, int val) {
        if (index > _size) return;                  // Do nothing if index is greater than the size
        if (index < 0) index = 0;                   // Treat negative index as 0
        LinkedNode *newNode = new LinkedNode(val);  // Create a new node
        LinkedNode *curr = _dummyHead;              // Start from the dummy head
        while (index--) curr = curr->next;          // Move to the correct position
        newNode->next = curr->next;                 // Insert the new node
        curr->next = newNode;
        _size++;                                    // Increase the size of the list
    }

    // Delete the index-th node in the linked list, if the index is valid
    void deleteAtIndex(int index) {
        if (index >= _size || index < 0) return;    // Do nothing if the index is invalid
        LinkedNode *curr = _dummyHead;              // Start from the dummy head
        while (index--) curr = curr->next;          // Move to the node before the one to be deleted
        LinkedNode *temp = curr->next;              // Temporarily store the node to be deleted
        curr->next = curr->next->next;              // Remove the node from the list
        delete temp;                                // Delete the node
        temp = nullptr;
        _size--;                                    // Decrease the size of the list
    }

private:
    int _size;                  // Size of the linked list
    LinkedNode *_dummyHead;     // Dummy head node to simplify operations
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

Another way of writing:

class MyLinkedList {
public:
    struct LinkedNode {
        int val;
        LinkedNode* next;
        LinkedNode(int val) : val(val), next(nullptr) {}
    };

    MyLinkedList() {
        dummyHead = new LinkedNode(0);
        _size = 0;
    }

    int get(int index) {
        if (index < 0 || index >= _size) return -1;
        auto curr = dummyHead->next;
        while (index--) curr = curr->next;
        return curr->val;
    }

    void addAtHead(int val) {
        addAtIndex(0, val);
    }

    void addAtTail(int val) {
        addAtIndex(_size, val);
    }

    void addAtIndex(int index, int val) {
        if (index > _size) return;
        if (index < 0) index = 0;

        auto curr = dummyHead;
        while (index--) curr = curr->next;

        auto toAdd = new LinkedNode(val);
        toAdd->next = curr->next;
        curr->next = toAdd;

        _size++;
    }

    void deleteAtIndex(int index) {
        if (index < 0 || index >= _size) return;

        auto curr = dummyHead;
        while (index--) curr = curr->next;

        curr->next = curr->next->next;
        _size--;
    }
private:
    int _size;
    LinkedNode* dummyHead;
};

Without Dummy Head⚓︎

class MyLinkedList {
public:
    // Node structure for the linked list
    struct Node {
        int val;   // Value of the node
        Node *next; // Pointer to the next node
        Node(int _val) : val(_val), next(nullptr) {} // Constructor to initialize node
    } *head; // Head pointer of the list

    // Constructor initializes the linked list
    MyLinkedList() {
        head = nullptr; // Initially, the list is empty, so head is null
    }

    // Function to get the value of the node at a given index
    int get(int index) {
        if (index < 0) return -1; // Return -1 for invalid index
        auto p = head; // Start from the head of the list
        for (int i = 0; i < index && p; i++) p = p->next; // Traverse the list to reach the desired index
        if (!p) return -1; // If p is null, index is out of bounds
        return p->val; // Return the value of the node at the index
    }

    // Function to add a node at the beginning of the list
    void addAtHead(int val) {
        auto curr = new Node(val); // Create a new node
        curr->next = head; // Link new node to the current head
        head = curr; // Update head to the new node
    }

    // Function to add a node at the end of the list
    void addAtTail(int val) {
        if (!head) { // If the list is empty
            head = new Node(val); // Create a new node and set it as head
        }
        else {
            auto p = head; // Start from the head
            while (p->next) p = p->next; // Traverse to the last node
            p->next = new Node(val); // Add new node at the end
        }
    }

    // Function to add a node at a specific index
    void addAtIndex(int index, int val) {
        if (index <= 0) { // If index is 0 or negative, add at head
            addAtHead(val);
        }
        else {
            int len = 0;
            for (auto p = head; p; p = p->next) len++; // Calculate the length of the list
            if (index == len) { // If index equals the length of the list, add at tail
                addAtTail(val);
            }
            else if (index < len) { // If index is within bounds
                auto p = head;
                for (int i = 0; i < index - 1; i++) p = p->next; // Find the node before the desired index
                auto curr = new Node(val); // Create a new node
                curr->next = p->next; // Insert the new node after p
                p->next = curr;
            }
        }
    }

    // Function to delete a node at a specific index
    void deleteAtIndex(int index) {
        int len = 0;
        for (auto p = head; p; p = p->next) len++; // Calculate the length of the list
        if (index >= 0 && index < len) { // If index is within bounds
            if (!index) { // If index is 0, delete the head
                head = head->next;
            }
            else {
                auto p = head;
                for (int i = 0; i < index - 1; i++) p = p->next; // Find the node before the one to delete
                p->next = p->next->next; // Delete the node at the index
            }
        }
    }
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */

Double Linked List⚓︎

Add at Index, Add at Head, and Add at Tail:

  • Find the predecessor and the successor of the node to insert. If the node is to be inserted at head, its predecessor is a sentinel head. If the node is to be inserted at the tail, its successor is a sentinel tail. (Use bidirectional search to perform faster.)
  • Insert the node by changing the links to the next and previous nodes.
1
2
3
4
toAdd->prev = pred;
toAdd->next = succ;
pred->next = toAdd;
succ->prev = toAdd;

Delete at Index:

  • Find the predecessor and successor.
  • Delete the node by changing the links to the next and previous nodes.
pred->next = succ;
succ->prev = pred;

Get:

  • Compare index and size - index to define the fastest way: starting from the head, or starting from the tail.
  • Go to the wanted node.
1
2
3
4
5
6
7
8
9
auto curr = head;
if (index + 1 > _size - index) {
    for (int i = 0; i < index + 1; i++)
        curr = curr->next;
} else {
    curr = tail;
    for (int i = 0; i < _size - index; i++)
        curr = curr->prev;
}

Complete code:

class MyLinkedList {
public:
    struct DoubleLinkedNode {
        int val;
        DoubleLinkedNode* prev;
        DoubleLinkedNode* next;
        DoubleLinkedNode(int _val = 0) : val(_val), prev(nullptr), next(nullptr) {}
    };

    MyLinkedList() {
        _size = 0;
        head = new DoubleLinkedNode(0);
        tail = new DoubleLinkedNode(0);
        // sentinel (dummy) nodes as pseudo-head and pseudo-tail
        head->next = tail;
        tail->prev = head;
    }

    // Destructor to free memory of all nodes including sentinel nodes
    ~MyLinkedList() {
        auto curr = head;
        while (curr) {
            auto temp = curr;
            curr = curr->next;
            delete temp;
        }
    }

    int get(int index) {
        if (index < 0 || index >= _size) return -1;

        auto curr = head;
        if (index + 1 > _size - index) {
            for (int i = 0; i < index + 1; i++)
                curr = curr->next;
        } else {
            curr = tail;
            for (int i = 0; i < _size - index; i++)
                curr = curr->prev;
        }

        return curr->val;
    }

    void addAtHead(int val) {
        addAtIndex(0, val);
        // DoubleLinkedNode* pred = head, * succ = head->next;
        // _size++;
        // auto toAdd = new DoubleLinkedNode(val);
        // toAdd->prev = pred;
        // toAdd->next = succ;
        // pred->next = toAdd;
        // succ->prev = toAdd;
    }

    void addAtTail(int val) {
        addAtIndex(_size, val);
        // DoubleLinkedNode* succ = tail, * pred = tail->prev;
        // _size++;
        // auto toAdd = new DoubleLinkedNode(val);
        // toAdd->prev = pred;
        // toAdd->next = succ;
        // pred->next = toAdd;
        // succ->prev = toAdd;
    }

    void addAtIndex(int index, int val) {
        if (index > _size) return;
        if (index < 0) index = 0;

        DoubleLinkedNode* pred, * succ;
        if (index < _size - index) {
            pred = head;
            for (int i = 0; i < index; i++) 
                pred = pred->next;
            succ = pred->next;
        } else {
            succ = tail;
            for (int i = 0; i < _size - index; i++)
                succ = succ->prev;
            pred = succ->prev;
        }

        _size++;
        auto toAdd = new DoubleLinkedNode(val);
        toAdd->prev = pred;
        toAdd->next = succ;
        pred->next = toAdd;
        succ->prev = toAdd;
    }

    void deleteAtIndex(int index) {
        if (index < 0 || index >= _size) return;

        DoubleLinkedNode* pred, * succ;
        if (index < _size - index) {
            pred = head;
            for (int i = 0; i < index; i++)
                pred = pred->next;
            succ = pred->next->next;
        } else {
            succ = tail;
            for (int i = 0; i < _size - index - 1; i++)
                succ = succ->prev;
            pred = succ->prev->prev;
        }

        _size--;

        // Remove the node from the list
        // pred->next = succ;
        // succ->prev = pred;
        auto toDelete = pred->next;
        pred->next = succ;
        succ->prev = pred;
        delete toDelete; // Free the memory of the node to be deleted
    }

private:
    int _size;
    DoubleLinkedNode* head;
    DoubleLinkedNode* tail;
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList* obj = new MyLinkedList();
 * int param_1 = obj->get(index);
 * obj->addAtHead(val);
 * obj->addAtTail(val);
 * obj->addAtIndex(index,val);
 * obj->deleteAtIndex(index);
 */