Skip to content
DSA linked lists 8 min read

Doubly Linked List: Two-Way Nodes and Operations

A doubly linked list is a linked list whose nodes carry two pointers — prev and next — so you can walk the chain forward and backward. That extra back-link makes it possible to delete a node, or insert before it, in O(1) when you already hold the node, which a singly linked list cannot do. The cost is one more pointer per node and more links to keep consistent.

The node structure

Each node stores its value plus links to both neighbors. The head’s prev and the tail’s next are null.

struct Node {
    int data;
    Node* prev;
    Node* next;
    Node(int v) : data(v), prev(nullptr), next(nullptr) {}
};

Insert at the front

Link the new node to the old head in both directions, then move the head. O(1).

Node* pushFront(Node* head, int value) {
    Node* node = new Node(value);
    node->next = head;
    if (head != nullptr) head->prev = node;
    return node; // new head
}

Delete a node in O(1)

Here the back-link pays off. Given any node, splice it out by joining its neighbors directly — no need to walk from the head to find the predecessor.

Node* removeNode(Node* head, Node* node) {
    if (node->prev != nullptr) node->prev->next = node->next;
    else head = node->next;                 // node was the head
    if (node->next != nullptr) node->next->prev = node->prev;
    delete node;
    return head;
}

Traverse backward

Because each node knows its predecessor, you can print the list in reverse starting from the tail — impossible in a singly list without extra work.

void printReverse(Node* tail) {
    for (Node* cur = tail; cur != nullptr; cur = cur->prev)
        std::cout << cur->data << " ";
}

Complexity and trade-offs

OperationSinglyDoubly
Insert at frontO(1)O(1)
Delete given a nodeO(n)*O(1)
Traverse backwardO(n) with extra workO(1) per step
Memory per node1 pointer2 pointers

*Singly needs the previous node, so you must walk from the head.

For beginners: The golden rule is to fix up both directions. Whenever you change a node’s next, ask “what should the other node’s prev be now?” — and vice versa. A half-updated link silently corrupts the list.

Going deeper: A common production technique is a sentinel (dummy) head and tail node. With permanent boundary nodes, no operation ever hits a null neighbor, so you can drop the special cases above and the code becomes uniform. This is how an LRU cache keeps its eviction list.

When to use a doubly linked list

Choose a doubly linked list when you need to traverse both ways, or remove/insert relative to a node you already hold in O(1) — the backbone of a deque and an LRU cache. If you only ever move forward, the lighter singly linked list saves a pointer per node.

Next: the circular linked list, or the full set of linked list operations.

Last updated June 25, 2026
Was this helpful?