A doubly linked list node contains:
- An integer
data - A
nextpointer to the next node - A
prevpointer to the previous node
class Node { public int data; public Node next; public Node prev;
// Full constructor public Node(int data, Node next, Node prev) { this.data = data; this.next = next; this.prev = prev; }
// Constructor for creating a standalone node (e.g., the last or only node) public Node(int data) { this(data, null, null); }}The LinkedList class itself is just a thin wrapper — the operations below are built around the
Node structure above.
Inserting Before the Head
Given the head of a doubly linked list and a value, insert a new node before the current head. The new node becomes the new head.
- Create a new node with the given value.
- Set its
nextto point to the current head. - Update the
prevof the current head to point to the new node. - Return the new node as the new head.
public Node insertBeforeHead(Node head, int val) { Node newHead = new Node(val, head, null); if (head != null) { head.prev = newHead; } return newHead;}Inserting Before the Tail
Given the head of a doubly linked list, insert a new node just before the tail.
- Traverse the list to find the tail (the last node).
- Identify the penultimate node (
tail.prev). - Create a new node, with its
nextpointing to the tail and itsprevpointing to the penultimate node. - Update the tail’s
prevand the penultimate node’snextto link to the new node.
public Node insertBeforeTail(Node head, int val) { if (head == null || head.next == null) { // List is empty or has only one node; inserting before tail is same as inserting before head return insertBeforeHead(head, val); }
Node current = head; while (current.next != null) { current = current.next; }
Node tail = current; Node penultimate = tail.prev;
Node newNode = new Node(val, tail, penultimate); penultimate.next = newNode; tail.prev = newNode;
return head;}Deleting the Last Node
Given the head of a doubly linked list, delete the last node (the tail).
- If the list is empty (
head == null), returnnull. - If the list contains only one node, deleting it makes the list empty — return
null. - Traverse the list to find the last node (
tail). - Identify the second-last node (
tail.prev). - Set the second-last node’s
nexttonull, removing the tail. - Disconnect
tail.prevto break the backward reference. - Return
head(which may or may not have changed).
public Node deleteTail(Node head) { if (head == null) return null; // Empty list if (head.next == null) return null; // Only one node
Node current = head; while (current.next != null) { current = current.next; }
Node tail = current; Node penultimate = tail.prev;
penultimate.next = null; tail.prev = null; // Optional: helps with garbage collection
return head;}Reversing the List
Given the head of a doubly linked list, reverse it in place so the head becomes the tail and vice versa.
- If the list is empty or has only one node, it’s already reversed — return the same head.
- Initialize
currentto the head andprevtonull. - Traverse the list, swapping each node’s
nextandprev, and movingcurrentforward by following the newprevpointer (which was originallynext). - After traversal,
prevholds the last node visited; itsprevfield (the old next of the last node) points to the new head. - Return
prev.prevas the new head.
public Node reverse(Node head) { if (head == null || head.next == null) return head;
Node current = head; Node prev = null;
while (current != null) { // Store original prev prev = current.prev;
// Swap next and prev pointers current.prev = current.next; current.next = prev;
// Move to the next node (which is current.prev after the swap) current = current.prev; }
// After the loop, prev holds the last node we visited // Its prev is the new head return prev.prev;}Reversing the List (Brute Force)
Reverse a doubly linked list without modifying the links, by only changing the data values.
- If the list is empty or has only one node, it’s already reversed — return the same head.
- Initialize
currentto the head and aStack<Integer>to hold values. - Traverse the list once, pushing every node’s
dataonto the stack. - Re-initialize
currentto the head. - Traverse again, popping a value off the stack and assigning it to
current.dataeach time. - Return the head — the structure is unchanged, but the data values are reversed.
public Node reverseBruteForce(Node head) { if (head == null || head.next == null) return head;
Stack<Integer> stack = new Stack<>(); Node current = head;
// First pass: push all data into the stack while (current != null) { stack.push(current.data); current = current.next; }
// Second pass: replace node data with values from the stack current = head; while (current != null) { current.data = stack.pop(); current = current.next; }
return head;}