How to Implement Doubly Linked List in JavaScript Easily
To implement a
doubly linked list in JavaScript, create a Node class with value, next, and prev properties, then build a DoublyLinkedList class to manage nodes with methods like append and remove. This structure allows traversal in both directions efficiently.Syntax
A doubly linked list uses two classes: Node and DoublyLinkedList.
Nodeholds the data and links to the next and previous nodes.DoublyLinkedListmanages the list with aheadandtail, and methods to add or remove nodes.
javascript
class Node { constructor(value) { this.value = value; this.next = null; this.prev = null; } } class DoublyLinkedList { constructor() { this.head = null; this.tail = null; } append(value) { const newNode = new Node(value); if (!this.head) { this.head = newNode; this.tail = newNode; } else { this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; } } remove(value) { let current = this.head; while (current) { if (current.value === value) { if (current.prev) { current.prev.next = current.next; } else { this.head = current.next; } if (current.next) { current.next.prev = current.prev; } else { this.tail = current.prev; } return true; } current = current.next; } return false; } }
Example
This example shows how to create a doubly linked list, add nodes, remove a node, and print the list forward and backward.
javascript
class Node { constructor(value) { this.value = value; this.next = null; this.prev = null; } } class DoublyLinkedList { constructor() { this.head = null; this.tail = null; } append(value) { const newNode = new Node(value); if (!this.head) { this.head = newNode; this.tail = newNode; } else { this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; } } remove(value) { let current = this.head; while (current) { if (current.value === value) { if (current.prev) { current.prev.next = current.next; } else { this.head = current.next; } if (current.next) { current.next.prev = current.prev; } else { this.tail = current.prev; } return true; } current = current.next; } return false; } printForward() { let current = this.head; let result = ''; while (current) { result += current.value + ' '; current = current.next; } console.log(result.trim()); } printBackward() { let current = this.tail; let result = ''; while (current) { result += current.value + ' '; current = current.prev; } console.log(result.trim()); } } const list = new DoublyLinkedList(); list.append(10); list.append(20); list.append(30); list.printForward(); // Output: 10 20 30 list.printBackward(); // Output: 30 20 10 list.remove(20); list.printForward(); // Output: 10 30 list.printBackward(); // Output: 30 10
Output
10 20 30
30 20 10
10 30
30 10
Common Pitfalls
Common mistakes include:
- Not updating
prevandnextpointers correctly when adding or removing nodes. - Forgetting to update
headortailwhen the first or last node changes. - Not handling empty list cases properly.
Always check if the node to remove is the head or tail and update pointers accordingly.
javascript
class DoublyLinkedListWrong { constructor() { this.head = null; this.tail = null; } // Wrong: Does not update prev pointer of new node append(value) { const newNode = { value, next: null, prev: null }; if (!this.head) { this.head = newNode; this.tail = newNode; } else { this.tail.next = newNode; // Missing: newNode.prev = this.tail; this.tail = newNode; } } } // Correct append method updates prev pointer class DoublyLinkedListRight { constructor() { this.head = null; this.tail = null; } append(value) { const newNode = { value, next: null, prev: null }; if (!this.head) { this.head = newNode; this.tail = newNode; } else { this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; } } }
Quick Reference
- Node: Holds
value,next, andprev. - append(value): Adds node to the end, updates
tail. - remove(value): Finds and removes node, updates neighbors and
head/tailif needed. - Always update
prevandnextpointers to keep list integrity.
Key Takeaways
A doubly linked list node stores links to both next and previous nodes.
Always update both next and prev pointers when adding or removing nodes.
Keep track of head and tail to allow efficient insertion and deletion.
Handle empty list and single-node list cases carefully.
Test traversal forward and backward to verify list integrity.