Insert at Beginning (Doubly Linked List)

Inserting at the front of a DLL is O(1) when you maintain head and tail. Allocate a node, set newNode->next = head, newNode->prev = NULL, wire the old head’s prev to the new node (if the list was not empty), then move head. If the list was empty, the new node is both head and tail.

C program (insert_begin updates head and tail)

The sample passes &head and &tail so both pointers stay correct after each insert.

#include <stdio.h>
#include <stdlib.h>

struct Node {
    int data;
    struct Node *prev;
    struct Node *next;
};

void insert_begin(struct Node **head, struct Node **tail, int data)
{
    struct Node *n = malloc(sizeof(struct Node));
    if (n == NULL)
        return;
    n->data = data;
    n->prev = NULL;
    n->next = *head;
    if (*head != NULL)
        (*head)->prev = n;
    else
        *tail = n;
    *head = n;
}

void print_list(struct Node *head)
{
    while (head != NULL) {
        printf("%d ", head->data);
        head = head->next;
    }
    printf("\n");
}

void free_list(struct Node *head)
{
    while (head != NULL) {
        struct Node *next = head->next;
        free(head);
        head = next;
    }
}

int main(void)
{
    struct Node *head = NULL;
    struct Node *tail = NULL;

    insert_begin(&head, &tail, 30);
    insert_begin(&head, &tail, 20);
    insert_begin(&head, &tail, 10);

    print_list(head);   /* 10 20 30 */

    free_list(head);
    return 0;
}

Pointer updates (non-empty list)

NULL ← [new] ↔ [old head] → …     new.prev = NULL, new.next = head
            ↑                      old_head.prev = new
         new head

Complexity

Operation Time Extra space
insert_beginO(1)O(1) for the new node
print_listO(n)O(1)

Output

After insert_begin(&head, &tail, 30), insert_begin(..., 20), and insert_begin(..., 10), then print_list(head):

10 20 30

Step-by-step execution table

Step Operation List state (forward) List state (backward) head points to tail points to
1 insert_begin(30) 30 30 Node 30 Node 30
2 insert_begin(20) 20 → 30 30 → 20 Node 20 Node 30
3 insert_begin(10) 10 → 20 → 30 30 → 20 → 10 Node 10 Node 30

Memory layout table

Address Node data prev next Points to (prev / next)
0x1000 Node A 10 NULL 0x1008 prev: NULL, next: Node B
0x1008 Node B 20 0x1000 0x1010 prev: Node A, next: Node C
0x1010 Node C 30 0x1008 NULL prev: Node B, next: NULL

Pointer variables

  • head = 0x1000 — points to Node A (first node).
  • tail = 0x1010 — points to Node C (last node).

Traversal path for print_list(head)

Forward-only walk using next until NULL:

Start at head (0x1000) → Node A [10]
    ↓ (next)
Node B [20]
    ↓ (next)
Node C [30]
    ↓ (next)
NULL → Stop

Output: 10 20 30

Key differences from circular linked list

Typical singly circular list (CLL) with a tail vs this open doubly linked list. See CLL Introduction for the ring model.

Feature Circular linked list Doubly linked list
Structure Single link (next only) in usual singly CLL Double links (prev & next)
Tail connection Points to head (closes ring) next is NULL (open chain)
Head connection Reached again after a full lap; no NULL “end” on last node prev == NULL on first node
Traversal One direction with a stop rule (e.g. back to start) Both directions possible via next / prev
Memory per node 1 pointer (singly) 2 pointers
Termination condition Return to head / lap complete next == NULL (forward from tail)

See also Insert at Beginning (SLL) · Insert at End (DLL) · DLL Introduction