Reversing a Linked List (Important Interview Question)

To reverse a singly linked list in place, keep three pointers—prev, cur, and next—and flip each cur->next to point backward. When cur becomes NULL, prev is the new head. This uses O(n) time and O(1) extra space (no new nodes).

C program (iterative, return new head)

The demo builds 10 20 30 with insert_begin (same pattern as insert at beginning), prints, calls reverse_list, then prints again. Use head = reverse_list(head); because the old head becomes the tail.

  • Always save cur->next in next before overwriting cur->next, or you lose the rest of the list.
  • Empty list (head == NULL) and one-node lists are handled by the same loop.
#include <stdio.h>
#include <stdlib.h>

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

struct Node *insert_begin(struct Node *head, int data)
{
    struct Node *newNode = malloc(sizeof(struct Node));
    if (newNode == NULL)
        return head;

    newNode->data = data;
    newNode->next = head;
    return newNode;
}

struct Node *reverse_list(struct Node *head)
{
    struct Node *prev = NULL;
    struct Node *cur = head;

    while (cur != NULL) {
        struct Node *next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }

    return prev;
}

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

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

    head = insert_begin(head, 30);
    head = insert_begin(head, 20);
    head = insert_begin(head, 10);

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

    head = reverse_list(head);

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

    return 0;
}

Program output

10 20 30
30 20 10

Overall program flow

Step Operation head / new head List state (values)
1 head = NULL NULL Empty list
2 head = insert_begin(head, 30) 0x1000 30
3 head = insert_begin(head, 20) 0x2000 20 30
4 head = insert_begin(head, 10) 0x3000 10 20 30
5 print_list(head) 0x3000 Output line: 10 20 30
6 head = reverse_list(head) Return 0x1000 (old tail) Logical order 30 20 10
7 print_list(head) 0x1000 Output line: 30 20 10

Each while iteration inside reverse_list

Starting list: head is 0x3000 and forward links are 0x3000 → 0x2000 → 0x1000 → NULL.

Iteration 1 (cur at 0x3000, data 10)

Step Operation Result
1next = cur->nextnext = 0x2000
2cur->next = prev0x3000->next = NULL (first node is now detached forward)
3prev = cur; cur = nextprev = 0x3000, cur = 0x2000

After iteration 1, reversed prefix is 10 alone; remainder still 20 → 30.

Iteration 2 (cur at 0x2000, data 20)

Step Operation Result
1next = cur->nextnext = 0x1000
2cur->next = prev0x2000->next = 0x3000 (points back to 10)
3prev = cur; cur = nextprev = 0x2000, cur = 0x1000

After iteration 2: reversed prefix 20 → 10; cur at tail 30.

Iteration 3 (cur at 0x1000, data 30)

Step Operation Result
1next = cur->nextnext = NULL
2cur->next = prev0x1000->next = 0x2000
3prev = cur; cur = nextprev = 0x1000, cur = NULL → loop ends

Return prev (0x1000) as the new head.

Memory layout (example addresses)

Before reverse_list (forward links)

Address Node data next (before)
0x3000 head 10 0x2000
0x2000 20 0x1000
0x1000 tail 30 NULL

After reverse_list (backward links along the new path)

Address Node data next (after)
0x1000 new head 30 0x2000
0x2000 20 0x3000
0x3000 new tail 10 NULL

Visual representation

Before reverse (same shape as after three insert_begin calls):

head (0x3000)
   │
   ▼
┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ data: 10     │    │ data: 20     │    │ data: 30     │
│ next: 0x2000 ┼───→│ next: 0x1000 ┼───→│ next: NULL   │
└──────────────┘    └──────────────┘    └──────────────┘

After reverse_list (new head at old tail):

head (0x1000)
   │
   ▼
┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ data: 30     │    │ data: 20     │    │ data: 10     │
│ next: 0x2000 ┼───→│ next: 0x3000 ┼───→│ next: NULL   │
└──────────────┘    └──────────────┘    └──────────────┘

Detailed step-by-step: iteration 2 of reverse_list

Middle iteration: cur is 0x2000 (20), prev is 0x3000 (10 already reversed).

Line Code Before After
1 struct Node *next = cur->next; cur = 0x2000 next = 0x1000
2 cur->next = prev; prev = 0x3000 0x2000->next = 0x3000
3 prev = cur; prev = 0x2000
4 cur = next; cur = 0x1000

Complexity summary

Operation Time Extra space
reverse_list (iterative) O(n) O(1) (pointer variables only)
insert_begin (build demo list) O(1) per call O(1) per new node
print_list O(n) O(1)
Whole program (build + print + reverse + print) O(n) O(n) nodes stored in the list

The key takeaway is that iterative reversal runs in one pass with constant extra space—a classic interview pattern. A recursive solution reverses the rest first then rewires the head, but uses O(n) call stack space.

Alternative: recursion reverse_list_r(head) that returns the new head of the reversed list and fixes each link on the unwind; or use a stack of nodes for explicit O(n) extra space.