Skip to content
  • Doubly Linked List · DSA Basics

Doubly Linked List

LearnViaNotes @learnvianotes· 26 min read

A doubly linked list gives every node a prev pointer in addition to next, so the list can be walked in both directions and any node can be removed in O(1) given only a reference to it. It costs one extra pointer per node and a little more bookkeeping.

A doubly linked list: every node knows both its neighbours
         head                                                   tail
          ↓                                                       ↓
     ┌────┬──────┬────┐     ┌────┬──────┬────┐     ┌────┬──────┬────┐
∅ ◀──┼ ●  │  10  │ ●──┼────▶│ ●  │  20  │ ●──┼────▶│ ●  │  30  │ ●──┼──▶ ∅
     └────┴──────┴────┘◀────┼─●  │      │    │◀────┼─●  │      │    │
      prev  data  next      └────┴──────┴────┘     └────┴──────┴────┘

None <-> 10 <-> 20 <-> 30 <-> None

Structure of a node and traversal in both directions

Linking two nodes now means setting two pointers: a.next = b and b.prev = a. Forgetting one of them is the classic doubly-linked bug — the list looks fine going forward and breaks going backward. Traversal backwards starts from the last node and follows prev.

Python
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

def show(head):
    parts, curr, last = [], head, None
    while curr is not None:
        parts.append(str(curr.data))
        last = curr                          # remember the tail for the way back
        curr = curr.next
    print("forward:", " <-> ".join(parts))
    parts, curr = [], last
    while curr is not None:                  # walk back along prev
        parts.append(str(curr.data))
        curr = curr.prev
    print("backward:", " <-> ".join(parts))

head = Node(10); temp1 = Node(20); temp2 = Node(30)
head.next = temp1;  temp1.prev = head        # every link is set in both directions
temp1.next = temp2; temp2.prev = temp1
show(head)
C++
#include <iostream>
using namespace std;

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

void show(Node* head) {
    Node* last = nullptr;
    cout << "forward:";
    for (Node* curr = head; curr; curr = curr->next) {
        cout << (curr == head ? " " : " <-> ") << curr->data;
        last = curr;                             // remember the tail for the way back
    }
    cout << "\nbackward:";
    for (Node* curr = last; curr; curr = curr->prev)   // walk back along prev
        cout << (curr == last ? " " : " <-> ") << curr->data;
    cout << "\n";
}

int main() {
    Node* head = new Node(10); Node* temp1 = new Node(20); Node* temp2 = new Node(30);
    head->next = temp1;  temp1->prev = head;     // every link is set in both directions
    temp1->next = temp2; temp2->prev = temp1;
    show(head);
}
Java
public class Main {
    static class Node {
        int data; Node prev, next;
        Node(int d) { data = d; }
    }

    static void show(Node head) {
        StringBuilder sb = new StringBuilder("forward:");
        Node last = null;
        for (Node curr = head; curr != null; curr = curr.next) {
            sb.append(curr == head ? " " : " <-> ").append(curr.data);
            last = curr;                             // remember the tail for the way back
        }
        System.out.println(sb);
        sb = new StringBuilder("backward:");
        for (Node curr = last; curr != null; curr = curr.prev)   // walk back along prev
            sb.append(curr == last ? " " : " <-> ").append(curr.data);
        System.out.println(sb);
    }

    public static void main(String[] args) {
        Node head = new Node(10), temp1 = new Node(20), temp2 = new Node(30);
        head.next = temp1;  temp1.prev = head;       // every link is set in both directions
        temp1.next = temp2; temp2.prev = temp1;
        show(head);
    }
}
JavaScript
class Node {
    constructor(data) { this.data = data; this.prev = null; this.next = null; }
}

function show(head) {
    const parts = [];
    let last = null;
    for (let curr = head; curr !== null; curr = curr.next) {
        parts.push(curr.data);
        last = curr;                                 // remember the tail for the way back
    }
    console.log("forward:", parts.join(" <-> "));
    const back = [];
    for (let curr = last; curr !== null; curr = curr.prev)   // walk back along prev
        back.push(curr.data);
    console.log("backward:", back.join(" <-> "));
}

const head = new Node(10), temp1 = new Node(20), temp2 = new Node(30);
head.next = temp1;  temp1.prev = head;               // every link is set in both directions
temp1.next = temp2; temp2.prev = temp1;
show(head);
Output
forward: 10 <-> 20 <-> 30
backward: 30 <-> 20 <-> 10

Time O(n) traversal · Space O(n) for n nodes

Advantages and disadvantages

OperationSingly linkedDoubly linkedWhy the difference
Insert at beginningO(1)O(1)Both just re-point the head; doubly also sets the old head's prev.
Insert at endO(n) / O(1) with tailO(n) / O(1) with tailSame walk unless a tail is kept.
Delete headO(1)O(1)Doubly also clears the new head's prev.
Delete lastO(n) even with tailO(1) with tailThe tail's prev gives the new last node for free.
Delete a given nodeO(n)O(1)The predecessor is node.prev; singly must search for it.
Insert before a given nodeO(n)O(1)Same reason.
ReverseO(n)O(n)Doubly: swap prev and next in every node.

Insert at the beginning

Python
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

def insert_at_beginning(head, data):
    temp = Node(data)
    temp.next = head                 # new → old head
    if head is not None:
        head.prev = temp             # old head → new (guard the empty list)
    return temp

def show(head):
    parts = []
    while head:
        parts.append(str(head.data)); head = head.next
    print(" <-> ".join(parts))

head = Node(10); t1 = Node(20); t2 = Node(30)
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1
show(head)
head = insert_at_beginning(head, 5); show(head)
head = insert_at_beginning(head, 1); show(head)
C++
#include <iostream>
using namespace std;

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

Node* insertAtBeginning(Node* head, int data) {
    Node* temp = new Node(data);
    temp->next = head;               // new → old head
    if (head) head->prev = temp;     // old head → new (guard the empty list)
    return temp;
}

void show(Node* head) {
    for (; head; head = head->next) cout << head->data << (head->next ? " <-> " : "\n");
}

int main() {
    Node* head = new Node(10); Node* t1 = new Node(20); Node* t2 = new Node(30);
    head->next = t1; t1->prev = head; t1->next = t2; t2->prev = t1;
    show(head);
    head = insertAtBeginning(head, 5); show(head);
    head = insertAtBeginning(head, 1); show(head);
}
Java
public class Main {
    static class Node {
        int data; Node prev, next;
        Node(int d) { data = d; }
    }

    static Node insertAtBeginning(Node head, int data) {
        Node temp = new Node(data);
        temp.next = head;                    // new → old head
        if (head != null) head.prev = temp;  // old head → new (guard the empty list)
        return temp;
    }

    static void show(Node head) {
        StringBuilder sb = new StringBuilder();
        for (; head != null; head = head.next) sb.append(head.data).append(head.next != null ? " <-> " : "");
        System.out.println(sb);
    }

    public static void main(String[] args) {
        Node head = new Node(10), t1 = new Node(20), t2 = new Node(30);
        head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
        show(head);
        head = insertAtBeginning(head, 5); show(head);
        head = insertAtBeginning(head, 1); show(head);
    }
}
JavaScript
class Node {
    constructor(data) { this.data = data; this.prev = null; this.next = null; }
}

function insertAtBeginning(head, data) {
    const temp = new Node(data);
    temp.next = head;                        // new → old head
    if (head !== null) head.prev = temp;     // old head → new (guard the empty list)
    return temp;
}

function show(head) {
    const parts = [];
    for (; head !== null; head = head.next) parts.push(head.data);
    console.log(parts.join(" <-> "));
}

let head = new Node(10), t1 = new Node(20), t2 = new Node(30);
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
show(head);
head = insertAtBeginning(head, 5); show(head);
head = insertAtBeginning(head, 1); show(head);
Output
10 <-> 20 <-> 30
5 <-> 10 <-> 20 <-> 30
1 <-> 5 <-> 10 <-> 20 <-> 30

Time O(1) · Space O(1)

Insert at the end

Python
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

def insert_end(head, x):
    temp = Node(x)
    if head is None:
        return temp                      # empty list: new node is the head
    curr = head
    while curr.next is not None:         # O(n) walk to the last node
        curr = curr.next
    curr.next = temp                     # last → new
    temp.prev = curr                     # new → last
    return head

def show(head):
    parts = []
    while head:
        parts.append(str(head.data)); head = head.next
    print(" <-> ".join(parts))

head = Node(10); t1 = Node(20); t2 = Node(30)
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1
head = insert_end(head, 40); show(head)
empty = insert_end(None, 40); show(empty)
C++
#include <iostream>
using namespace std;

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

Node* insertEnd(Node* head, int x) {
    Node* temp = new Node(x);
    if (!head) return temp;                  // empty list: new node is the head
    Node* curr = head;
    while (curr->next) curr = curr->next;    // O(n) walk to the last node
    curr->next = temp;                       // last → new
    temp->prev = curr;                       // new → last
    return head;
}

void show(Node* head) {
    for (; head; head = head->next) cout << head->data << (head->next ? " <-> " : "\n");
}

int main() {
    Node* head = new Node(10); Node* t1 = new Node(20); Node* t2 = new Node(30);
    head->next = t1; t1->prev = head; t1->next = t2; t2->prev = t1;
    head = insertEnd(head, 40); show(head);
    Node* empty = insertEnd(nullptr, 40); show(empty);
}
Java
public class Main {
    static class Node {
        int data; Node prev, next;
        Node(int d) { data = d; }
    }

    static Node insertEnd(Node head, int x) {
        Node temp = new Node(x);
        if (head == null) return temp;               // empty list: new node is the head
        Node curr = head;
        while (curr.next != null) curr = curr.next;  // O(n) walk to the last node
        curr.next = temp;                            // last → new
        temp.prev = curr;                            // new → last
        return head;
    }

    static void show(Node head) {
        StringBuilder sb = new StringBuilder();
        for (; head != null; head = head.next) sb.append(head.data).append(head.next != null ? " <-> " : "");
        System.out.println(sb);
    }

    public static void main(String[] args) {
        Node head = new Node(10), t1 = new Node(20), t2 = new Node(30);
        head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
        head = insertEnd(head, 40); show(head);
        Node empty = insertEnd(null, 40); show(empty);
    }
}
JavaScript
class Node {
    constructor(data) { this.data = data; this.prev = null; this.next = null; }
}

function insertEnd(head, x) {
    const temp = new Node(x);
    if (head === null) return temp;                  // empty list: new node is the head
    let curr = head;
    while (curr.next !== null) curr = curr.next;     // O(n) walk to the last node
    curr.next = temp;                                // last → new
    temp.prev = curr;                                // new → last
    return head;
}

function show(head) {
    const parts = [];
    for (; head !== null; head = head.next) parts.push(head.data);
    console.log(parts.join(" <-> "));
}

let head = new Node(10), t1 = new Node(20), t2 = new Node(30);
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
head = insertEnd(head, 40); show(head);
const empty = insertEnd(null, 40); show(empty);
Output
10 <-> 20 <-> 30 <-> 40
40

Time O(n) — O(1) with a tail pointer · Space O(1)

Delete the head

Python
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

def delete_head(head):
    if head is None or head.next is None:
        return None                      # 0 or 1 node → empty
    head = head.next
    head.prev = None                     # the new head has no predecessor
    return head

def show(head):
    parts = []
    while head:
        parts.append(str(head.data)); head = head.next
    print(" <-> ".join(parts) if parts else "empty")

head = Node(10); t1 = Node(20); t2 = Node(30)
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1
for _ in range(3):
    head = delete_head(head)
    print("after delete head: ", end=""); show(head)
C++
#include <iostream>
using namespace std;

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

Node* deleteHead(Node* head) {
    if (!head) return nullptr;
    if (!head->next) { delete head; return nullptr; }   // 1 node → empty
    Node* newHead = head->next;
    newHead->prev = nullptr;                 // the new head has no predecessor
    delete head;
    return newHead;
}

void show(Node* head) {
    if (!head) { cout << "empty\n"; return; }
    for (; head; head = head->next) cout << head->data << (head->next ? " <-> " : "\n");
}

int main() {
    Node* head = new Node(10); Node* t1 = new Node(20); Node* t2 = new Node(30);
    head->next = t1; t1->prev = head; t1->next = t2; t2->prev = t1;
    for (int i = 0; i < 3; i++) {
        head = deleteHead(head);
        cout << "after delete head: "; show(head);
    }
}
Java
public class Main {
    static class Node {
        int data; Node prev, next;
        Node(int d) { data = d; }
    }

    static Node deleteHead(Node head) {
        if (head == null || head.next == null) return null;   // 0 or 1 node → empty
        head = head.next;
        head.prev = null;                        // the new head has no predecessor
        return head;
    }

    static void show(Node head) {
        if (head == null) { System.out.println("empty"); return; }
        StringBuilder sb = new StringBuilder();
        for (; head != null; head = head.next) sb.append(head.data).append(head.next != null ? " <-> " : "");
        System.out.println(sb);
    }

    public static void main(String[] args) {
        Node head = new Node(10), t1 = new Node(20), t2 = new Node(30);
        head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
        for (int i = 0; i < 3; i++) {
            head = deleteHead(head);
            System.out.print("after delete head: "); show(head);
        }
    }
}
JavaScript
class Node {
    constructor(data) { this.data = data; this.prev = null; this.next = null; }
}

function deleteHead(head) {
    if (head === null || head.next === null) return null;    // 0 or 1 node → empty
    head = head.next;
    head.prev = null;                            // the new head has no predecessor
    return head;
}

function show(head) {
    const parts = [];
    for (; head !== null; head = head.next) parts.push(head.data);
    console.log(parts.length ? parts.join(" <-> ") : "empty");
}

let head = new Node(10), t1 = new Node(20), t2 = new Node(30);
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
for (let i = 0; i < 3; i++) {
    head = deleteHead(head);
    process.stdout.write("after delete head: "); show(head);
}
Output
after delete head: 20 <-> 30
after delete head: 30
after delete head: empty

Time O(1) · Space O(1)

Delete the last node

Python
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

def delete_last(head):
    if head is None or head.next is None:
        return None                      # 0 or 1 node → empty
    curr = head
    while curr.next is not None:         # walk to the last node (O(1) with a tail pointer)
        curr = curr.next
    curr.prev.next = None                # predecessor is one hop away via prev
    return head

def show(head):
    parts = []
    while head:
        parts.append(str(head.data)); head = head.next
    print(" <-> ".join(parts) if parts else "empty")

head = Node(10); t1 = Node(20); t2 = Node(30)
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1
for _ in range(3):
    head = delete_last(head)
    print("after delete last: ", end=""); show(head)
C++
#include <iostream>
using namespace std;

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

Node* deleteLast(Node* head) {
    if (!head) return nullptr;
    if (!head->next) { delete head; return nullptr; }   // 1 node → empty
    Node* curr = head;
    while (curr->next) curr = curr->next;    // walk to the last node (O(1) with a tail pointer)
    curr->prev->next = nullptr;              // predecessor is one hop away via prev
    delete curr;
    return head;
}

void show(Node* head) {
    if (!head) { cout << "empty\n"; return; }
    for (; head; head = head->next) cout << head->data << (head->next ? " <-> " : "\n");
}

int main() {
    Node* head = new Node(10); Node* t1 = new Node(20); Node* t2 = new Node(30);
    head->next = t1; t1->prev = head; t1->next = t2; t2->prev = t1;
    for (int i = 0; i < 3; i++) {
        head = deleteLast(head);
        cout << "after delete last: "; show(head);
    }
}
Java
public class Main {
    static class Node {
        int data; Node prev, next;
        Node(int d) { data = d; }
    }

    static Node deleteLast(Node head) {
        if (head == null || head.next == null) return null;   // 0 or 1 node → empty
        Node curr = head;
        while (curr.next != null) curr = curr.next;   // walk to the last node (O(1) with a tail pointer)
        curr.prev.next = null;                        // predecessor is one hop away via prev
        return head;
    }

    static void show(Node head) {
        if (head == null) { System.out.println("empty"); return; }
        StringBuilder sb = new StringBuilder();
        for (; head != null; head = head.next) sb.append(head.data).append(head.next != null ? " <-> " : "");
        System.out.println(sb);
    }

    public static void main(String[] args) {
        Node head = new Node(10), t1 = new Node(20), t2 = new Node(30);
        head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
        for (int i = 0; i < 3; i++) {
            head = deleteLast(head);
            System.out.print("after delete last: "); show(head);
        }
    }
}
JavaScript
class Node {
    constructor(data) { this.data = data; this.prev = null; this.next = null; }
}

function deleteLast(head) {
    if (head === null || head.next === null) return null;    // 0 or 1 node → empty
    let curr = head;
    while (curr.next !== null) curr = curr.next;     // walk to the last node (O(1) with a tail pointer)
    curr.prev.next = null;                           // predecessor is one hop away via prev
    return head;
}

function show(head) {
    const parts = [];
    for (; head !== null; head = head.next) parts.push(head.data);
    console.log(parts.length ? parts.join(" <-> ") : "empty");
}

let head = new Node(10), t1 = new Node(20), t2 = new Node(30);
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
for (let i = 0; i < 3; i++) {
    head = deleteLast(head);
    process.stdout.write("after delete last: "); show(head);
}
Output
after delete last: 10 <-> 20
after delete last: 10
after delete last: empty

Time O(n) — O(1) with a tail pointer · Space O(1)

Delete a node given its reference

Deleting a node in O(1): two pointer writes on its neighbours
before:   [10] ◀──▶ [20] ◀──▶ [30]
                    node

node.prev.next = node.next     [10] ───────▶ [30]       (10's next skips 20)
node.next.prev = node.prev     [10] ◀─────── [30]       (30's prev skips 20)

after:    [10] ◀──▶ [30]
Python
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

def delete_node(head, node):
    if node.prev is not None:
        node.prev.next = node.next       # predecessor skips over node
    else:
        head = node.next                 # node was the head
    if node.next is not None:
        node.next.prev = node.prev       # successor skips back over node
    return head

def show(head):
    parts = []
    while head:
        parts.append(str(head.data)); head = head.next
    print(" <-> ".join(parts) if parts else "empty")

head = Node(10); t1 = Node(20); t2 = Node(30)
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1
show(head)
head = delete_node(head, t1); print("delete node 20: ", end=""); show(head)
head = delete_node(head, head); print("delete node 10 (head): ", end=""); show(head)
C++
#include <iostream>
using namespace std;

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

Node* deleteNode(Node* head, Node* node) {
    if (node->prev) node->prev->next = node->next;   // predecessor skips over node
    else head = node->next;                          // node was the head
    if (node->next) node->next->prev = node->prev;   // successor skips back over node
    delete node;
    return head;
}

void show(Node* head) {
    if (!head) { cout << "empty\n"; return; }
    for (; head; head = head->next) cout << head->data << (head->next ? " <-> " : "\n");
}

int main() {
    Node* head = new Node(10); Node* t1 = new Node(20); Node* t2 = new Node(30);
    head->next = t1; t1->prev = head; t1->next = t2; t2->prev = t1;
    show(head);
    head = deleteNode(head, t1); cout << "delete node 20: "; show(head);
    head = deleteNode(head, head); cout << "delete node 10 (head): "; show(head);
}
Java
public class Main {
    static class Node {
        int data; Node prev, next;
        Node(int d) { data = d; }
    }

    static Node deleteNode(Node head, Node node) {
        if (node.prev != null) node.prev.next = node.next;   // predecessor skips over node
        else head = node.next;                               // node was the head
        if (node.next != null) node.next.prev = node.prev;   // successor skips back over node
        return head;
    }

    static void show(Node head) {
        if (head == null) { System.out.println("empty"); return; }
        StringBuilder sb = new StringBuilder();
        for (; head != null; head = head.next) sb.append(head.data).append(head.next != null ? " <-> " : "");
        System.out.println(sb);
    }

    public static void main(String[] args) {
        Node head = new Node(10), t1 = new Node(20), t2 = new Node(30);
        head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
        show(head);
        head = deleteNode(head, t1); System.out.print("delete node 20: "); show(head);
        head = deleteNode(head, head); System.out.print("delete node 10 (head): "); show(head);
    }
}
JavaScript
class Node {
    constructor(data) { this.data = data; this.prev = null; this.next = null; }
}

function deleteNode(head, node) {
    if (node.prev !== null) node.prev.next = node.next;   // predecessor skips over node
    else head = node.next;                                // node was the head
    if (node.next !== null) node.next.prev = node.prev;   // successor skips back over node
    return head;
}

function show(head) {
    const parts = [];
    for (; head !== null; head = head.next) parts.push(head.data);
    console.log(parts.length ? parts.join(" <-> ") : "empty");
}

let head = new Node(10), t1 = new Node(20), t2 = new Node(30);
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
show(head);
head = deleteNode(head, t1); process.stdout.write("delete node 20: "); show(head);
head = deleteNode(head, head); process.stdout.write("delete node 10 (head): "); show(head);
Output
10 <-> 20 <-> 30
delete node 20: 10 <-> 30
delete node 10 (head): 30

Time O(1) · Space O(1)

Reverse a doubly linked list

  1. Walk the list

    curr = head, remembering the node just processed in prev.

  2. Swap in place

    curr.next, curr.prev = curr.prev, curr.next.

  3. Advance

    curr = curr.prev — that field now holds the old next.

  4. Finish

    When curr is None, prev is the last node processed: the new head.

Python
class Node:
    def __init__(self, data):
        self.data = data
        self.prev = None
        self.next = None

def reverse_dll(head):
    prev, curr = None, head
    while curr is not None:
        prev = curr
        curr.next, curr.prev = curr.prev, curr.next   # swap the two links
        curr = curr.prev                              # old next now lives in prev
    return prev                                       # last node processed = new head

def show(label, head):
    parts = []
    while head:
        parts.append(str(head.data)); head = head.next
    print(label + ":", " <-> ".join(parts))

head = Node(10); t1 = Node(20); t2 = Node(30)
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1
show("forward", head)
head = reverse_dll(head); show("reversed", head)
head = reverse_dll(head); show("back again", head)
C++
#include <iostream>
#include <utility>
using namespace std;

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

Node* reverseDLL(Node* head) {
    Node* prev = nullptr;
    for (Node* curr = head; curr; curr = curr->prev) {   // after the swap, prev holds the old next
        prev = curr;
        swap(curr->next, curr->prev);                      // swap the two links
    }
    return prev;                                           // last node processed = new head
}

void show(const char* label, Node* head) {
    cout << label << ":";
    for (; head; head = head->next) cout << " " << head->data << (head->next ? " <->" : "");
    cout << "\n";
}

int main() {
    Node* head = new Node(10); Node* t1 = new Node(20); Node* t2 = new Node(30);
    head->next = t1; t1->prev = head; t1->next = t2; t2->prev = t1;
    show("forward", head);
    head = reverseDLL(head); show("reversed", head);
    head = reverseDLL(head); show("back again", head);
}
Java
public class Main {
    static class Node {
        int data; Node prev, next;
        Node(int d) { data = d; }
    }

    static Node reverseDLL(Node head) {
        Node prev = null;
        for (Node curr = head; curr != null; curr = curr.prev) {   // after the swap, prev holds the old next
            prev = curr;
            Node t = curr.next; curr.next = curr.prev; curr.prev = t;   // swap the two links
        }
        return prev;                                               // last node processed = new head
    }

    static void show(String label, Node head) {
        StringBuilder sb = new StringBuilder(label + ":");
        for (; head != null; head = head.next) sb.append(" ").append(head.data).append(head.next != null ? " <->" : "");
        System.out.println(sb);
    }

    public static void main(String[] args) {
        Node head = new Node(10), t1 = new Node(20), t2 = new Node(30);
        head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
        show("forward", head);
        head = reverseDLL(head); show("reversed", head);
        head = reverseDLL(head); show("back again", head);
    }
}
JavaScript
class Node {
    constructor(data) { this.data = data; this.prev = null; this.next = null; }
}

function reverseDLL(head) {
    let prev = null;
    for (let curr = head; curr !== null; curr = curr.prev) {   // after the swap, prev holds the old next
        prev = curr;
        [curr.next, curr.prev] = [curr.prev, curr.next];        // swap the two links
    }
    return prev;                                                // last node processed = new head
}

function show(label, head) {
    const parts = [];
    for (; head !== null; head = head.next) parts.push(head.data);
    console.log(label + ":", parts.join(" <-> "));
}

let head = new Node(10), t1 = new Node(20), t2 = new Node(30);
head.next = t1; t1.prev = head; t1.next = t2; t2.prev = t1;
show("forward", head);
head = reverseDLL(head); show("reversed", head);
head = reverseDLL(head); show("back again", head);
Output
forward: 10 <-> 20 <-> 30
reversed: 30 <-> 20 <-> 10
back again: 10 <-> 20 <-> 30

Time O(n) · Space O(1)

Interview Q&A

What does the extra prev pointer buy you?

How does an LRU cache use a doubly linked list?

Singly vs doubly linked list — how do you choose?

How would you reverse a doubly linked list?

What is an XOR linked list?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report