Skip to content
DSA linked lists 9 min read

Linked List Operations: Insert, Delete, and Search

The core operations on a linked list are inserting and deleting nodes (at the head, tail, or a middle position) and searching for a value. Each comes down to carefully rewiring next pointers in the right order. This page covers every case with its complexity, and introduces the dummy-head trick that makes the messy edge cases disappear. Examples use a singly linked list node.

Insert at the head — O(1)

Point the new node at the current head, then return it as the new head. No walking needed.

Node* insertHead(Node* head, int value) {
    Node* node = new Node(value);
    node->next = head;
    return node;
}

Insert at the tail — O(n)

Walk to the last node, then attach. (With a stored tail pointer this is O(1), but the bare version must traverse.)

Node* insertTail(Node* head, int value) {
    Node* node = new Node(value);
    if (head == nullptr) return node;
    Node* cur = head;
    while (cur->next != nullptr) cur = cur->next;
    cur->next = node;
    return head;
}

Insert at a middle position — O(n)

To insert at 0-based index pos, stop at the node before it, then splice. Order matters: set the new node’s next before rewiring the predecessor, or you lose the rest of the list.

Node* insertAt(Node* head, int pos, int value) {
    if (pos <= 0 || head == nullptr) return insertHead(head, value);
    Node* cur = head;
    for (int i = 0; i < pos - 1 && cur->next != nullptr; i++)
        cur = cur->next;
    Node* node = new Node(value);
    node->next = cur->next;   // first
    cur->next = node;         // then
    return head;
}

Delete by value with a dummy head — O(n)

Deletion is where edge cases bite: removing the head is different from removing a middle node. The fix is a dummy node that sits before the real head, so every node has a predecessor and the head case stops being special. Return dummy.next as the new head.

Node* deleteValue(Node* head, int target) {
    Node dummy(0);
    dummy.next = head;
    Node* prev = &dummy;
    while (prev->next != nullptr) {
        if (prev->next->data == target) {
            Node* doomed = prev->next;
            prev->next = doomed->next;   // unlink
            delete doomed;
            break;                       // remove first match only
        }
        prev = prev->next;
    }
    return dummy.next;
}

Search — O(n)

Walk forward, returning the 0-based index of the first match or -1.

int indexOf(Node* head, int target) {
    int i = 0;
    for (Node* cur = head; cur != nullptr; cur = cur->next, i++)
        if (cur->data == target) return i;
    return -1;
}

Complexity at a glance

OperationTimeSpace
Insert/delete at headO(1)O(1)
Insert/delete at tailO(n)*O(1)
Insert/delete at indexO(n)O(1)
Search by valueO(n)O(1)

*O(1) if you maintain a tail pointer.

For beginners: The rule that prevents most bugs — when relinking, save the node you’re about to skip before you overwrite its pointer. Once you reassign cur->next, the old successor is unreachable unless you grabbed it first.

Going deeper: The dummy-head pattern generalizes far beyond deletion. Any time the head might change — merging two sorted lists, partitioning, removing the Nth node — start from a dummy and return dummy.next. It removes an entire class of null-handling bugs.

Next: apply these to reversing a linked list and the fast & slow pointers technique, then test yourself with the exercises.

Last updated June 25, 2026
Was this helpful?