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_begin | O(1) | O(1) for the new node |
print_list | O(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