A doubly linked list (sometimes also called double linked list) is a type of linked list data structure that has two references in each node:
- The
next
reference that points to the next node - The
previous
reference that points to the previous node
Just like a singly linked list, a doubly linked list also has the head
and tail
references that point to the first and last node in the list respectively.
Here’s an illustration of a doubly linked list data structure:
If you need a refresher on the linked list data structure, you can visit my linked list tutorial here:
JavaScript linked list data structure in five easy steps
This tutorial will expand on the singly LinkedList
class implementation on the tutorial above.
The code for this tutorial can be downloaded from the DoublyLinkedListJS GitHub Gist.
Creating the doubly linked list class
The doubly linked list is very similar to the singly linked list. The only difference between the two is that a doubly linked list has the previous
pointer in each node.
First, you need to update the function that creates the node as shown below:
function createNode(value) {
return {
value: value,
next: null,
previous: null,
};
}
Next, create the DoublyLinkedList
class as follows:
class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
}
With that, you can start writing code that will insert and print the DoublyLinkedList
instance content.
Adding insert method to the doubly linked list
The code for the insert()
method is as follows:
insert(value) {
this.length++;
let newNode = createNode(value);
if (this.tail) {
// list is not empty
this.tail.next = newNode;
newNode.previous = this.tail;
this.tail = newNode;
return newNode;
}
this.head = this.tail = newNode;
return newNode;
}
The insert()
method of the list will accept one parameter: the value
that will be stored in the node.
Each time a new node is inserted into the list, the length
property of the list will go up by one and a new node is created using the createNode()
function.
If the list is not empty, then the list needs to be adjusted with the following steps:
- The current tail
next
reference points to thenewNode
- The
newNode.previous
refers to the currentthis.tail
- Then
this.tail
will point to thenewNode
When the list is empty, then the head
and tail
reference points to the newNode
Next, let’s create a print()
method to print each node value.
Doubly linked list print method
The code for the print()
method is as follows:
print() {
let current = this.head;
while (current) {
console.log(
`${current.previous?.value} ${current.value} ${current.next?.value}`
);
current = current.next;
}
}
The method above will print the current
node’s value, along with the value of the previous
and next
pointer when they are available.
You can test the list implementation with the following code:
const dLinkedList = new DoublyLinkedList();
dLinkedList.insert(7);
dLinkedList.insert(8);
dLinkedList.insert(9);
dLinkedList.print();
The console output should be like this:
undefined 7 8
7 8 9
8 9 undefined
It means, the insert()
and print()
methods are working correctly. Nice job!
Doubly linked list remove method
The code for the remove()
method of the list is as follows:
remove() {
if (this.tail) {
this.length--;
const removedTail = this.tail;
this.tail = this.tail.previous;
if(this.tail){
this.tail.next = null;
} else {
this.head = null;
}
return removedTail;
}
return undefined;
}
First, you need to check that the tail
reference is not null
. After that, you can start by decrementing the length
property by one.
Next, keep a reference to the current tail so you can return it later. Then adjust the this.tail
reference to point to the previous
node.
Finally, check if the new tail is null
or not. If the new tail is not null
, cut off the previous tail by setting the this.tail.next
property to null.
If the new tail is null
, then adjust the this.head
property to refer to null
as well.
Return the removedTail
once the execution is completed.
Now you have the insert()
and remove()
methods working, you can test to remove a node from the list instance.
Insert and remove a node from the head
The insertHead()
method is a method that allows you to insert a node from the head.
The code for the method is as shown below:
insertHead(value) {
this.length++;
let newNode = createNode(value);
if (this.head) {
this.head.previous = newNode;
newNode.next = this.head;
this.head = newNode;
return newNode;
}
this.head = this.tail = newNode;
return newNode;
}
It’s very similar to the insert()
method, but instead of adjusting the tail
reference, you adjust the head
reference of the list.
The same goes for the removeHead()
method:
removeHead() {
if (this.head) {
this.length--;
const removedHead = this.head;
this.head = this.head.next;
if(this.head){
this.head.previous = null;
} else {
this.tail = null;
}
return removedHead;
}
return undefined;
}
Now your doubly linked list implementation should be able to insert and remove a node from the head.
Bonus: insert and remove at specific index
One difference between a linked list and an array is that a linked list doesn’t store the index of the nodes.
To insert and remove a node at a specific index, you need to traverse the node until you reach the node at that index.
Here’s the code for inserting and removing a node at a specific index:
insertIndex(value, index) {
if (index >= this.length) {
throw new Error("Insert index out of bounds");
}
if (index === 0) {
return this.insertHead(value);
}
this.length++;
let currentNode = this.head;
for (let i = 0; i < index; i++) {
currentNode = currentNode.next;
}
const previousNode = currentNode.previous;
const newNode = createNode(value);
newNode.next = currentNode;
newNode.previous = previousNode;
previousNode.next = newNode;
currentNode.previous = newNode;
return newNode;
}
// remove at specific index
removeIndex(index) {
if (index >= this.length) {
throw new Error("Remove index out of bounds");
}
if (index === 0) {
return this.removeHead();
}
this.length--;
let currentNode = this.head;
for (let i = 0; i < index; i++) {
currentNode = currentNode.next;
}
const previousNode = currentNode.previous;
const nextNode = currentNode.next;
previousNode.next = nextNode;
nextNode.previous = previousNode;
return currentNode;
}
Here’s the complete code for the DoublyLinkedList
class again in case you need it.
Conclusion
You have just completed a doubly linked list implementation using JavaScript. Nicely done!
A doubly linked list is just a linked list with an extra reference in each node that points to the previous value.
Because each node knows about the previous node, you can remove nodes from the list more efficiently than a singly linked list.
Thanks for reading this tutorial 😉