Table of contents
What is Linked List? ๐๏ธ
A linked list is a linear data structure that includes a series of connected nodes. Here, each node stores the data and the address of the next node. For example,
image credits: programiz.com
Important Concept ๐๏ธ
- The linked list can be used in almost every situation where a 1D array is used.
- The first node of the linked list is called
head
. - The last node of the linked list is called
tail
. tail
has always null as thenext
reference because it's the last element.- The
next
is always pointing to the successor node in the list.
Types of Linked List ๐๏ธ
Singly Linked List
In a Singly Linked List, the node has only one reference which stores the address of the next node in the list. We are going to cover it in this article.
Doubly Linked List
In Doubly Linked List, the node has two references. One to the next node and another to the previous node.
Circular Linked List
In Circular Linked List, the node may have one or two references. But the tail
will be connected to the head
of the Linked List.
Time Complexity ๐๏ธ
Operation | Time Complexity |
Insertion | O(1) |
Deletion | O(1) |
Search | O(n) |
Space Complexity of Linked List: O(n)
Applications of Linked List ๐๏ธ
- Linked List is heavily used to create stacks and queues.
- When you need constant-time insertions or deletions from the list.
- When the size of the list is unknown. This means you don't know how much your list is going to be scaled, then use Linked List instead of Arrays.
- To implement Hash Tables, Trees or Graphs
Advantages of Linked List ๐๏ธ
- Using linked list efficient memory utilization can be achieved since the size of linked list can be change at the run time.
- Stacks and queues are often easily implemented using a linked list.
- Insertion and deletion are quite easier in the linked list.
Disadvantages of Linked List ๐๏ธ
- More memory is required in the linked list compared to an array.
- Traversal in linked list is more time consuming.
- In a singly linked list reverse traversing is not possible, but it can be done using doubly linked list which will take even more memory.
- Random access is not possible due to its dynamic memory allocation.
Coding a Linked List in JavaScript ๐๏ธ
At first a node class should be created which is going to represent a node in the linked list.
class Node {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
We have created a Node
class and inside the constructor
, we have two parameters which are the properties of a node.
value
- It is the value of the node.next
- This will contain the reference of the next node. Initially, it is alwaysnull
.
After creating the structure of the node, we can now create the linked list.
Let's create a LinkedList
class that will contain the operations of our linked list.
class LinkedList {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
}
We have created a class LinkedList
and inside the constructor
, we are initializing three things.
head
: The first node of the linked list.tail
: The last node of the linked list.length
: The length of the linked list.
โ Let's code some operations of the linked list!
push
In push
, we insert a node at the end of the linked list.
The steps which we are going to follow while coding push
.
- Create
push
function in the class that accepts avalue
as an argument. - Create a new
node
using theNode
class while passing thevalue
. - Check if the list is empty:
- If the list is empty, then assign the new node to the
head
andtail
. Since there is only one node which is both thehead
andtail
in the list.
- If the list is empty, then assign the new node to the
- If the list is not empty:
- Assign the
next
property of the currenttail
to the new node. - Assign the new node to the
tail
itself. - Increment the
length
by one.
- Assign the
push( value ) {
// create node
let node = new Node( value );
/** check if list is empty */
if( !this.head ) { // if empty
this.head = node; // new head
this.tail = node; // new tail
} else {
this.tail.next = node; // reference to the new node
this.tail = node; // update the tail to the new node
}
this.length++;
return this;
}
pop
In pop
, we delete a node from the end of the linked list.
Here are the steps which we are going to follow while coding pop
.
- Create
pop
function in the class that accepts nothing. - Check if the list is empty:
- If the list is empty, then return
- If the list is not empty:
- We have to calculate second last element so that we can set
tail
of the linked list to the second last element and remove the last one by setting the next property tonull
on the currenttail
. - Decrement the
length
by one. - If the
length
of the linked list is zero, that means the linked list is empty. So we set thehead
andtail
tonull
.
- We have to calculate second last element so that we can set
pop() {
// if head is null, then return null
if( !this.head ) {
return;
}
// find the second last node.
let currentNode = this.head,
secondLastNode;
while( currentNode.next ) {
secondLastNode = currentNode;
currentNode = currentNode.next;
}
// set the second last node to the new tail of the linked list.
this.tail = secondLastNode;
// set the current tail's next to null
this.tail.next = null;
this.length--;
// if the length of linked list becomes 0, then reset the linked list
if( this.length === 0 ) {
this.head = null;
this.tail = null;
}
return this;
}
shift
In shift
, we remove a node at the start of the linked list.
Here are the steps which we are going to follow while coding shift
.
- Create a function
shift
in the class which is going to accept nothing. - Check if the list is empty:
- If the list is empty, then return.
- If the list is not empty:
- We store the current
head
in a variable calledremoveHead
. - We will set the current
head
to thenext
of theremoveHead
. - Decrement the
length
by one. - If the
length
of the linked list is zero, that means the linked list is empty. So we set thehead
andtail
tonull
.
- We store the current
shift() {
// If there is no head then return
if( !this.head ) {
return;
}
// change the current head
let removeHead = this.head;
this.head = removeHead.next;
this.length--;
// if the length of linked list becomes 0, then reset the linked list
if( this.length === 0 ) {
this.head = null;
this.tail = null;
}
return this;
}
unshift
In unshift
, we add a node at the start of the linked list.
Here are the steps which we are going to follow while coding unshift
.
- Create a method
unshift
in the class which is going to accept thevalue
of the node as argument. - Creating a new node from the
Node
class while passing thevalue
. - Check if the list is empty:
- If the list is empty, then assign the new node to the
head
andtail
. Since there is only one node which is both thehead
andtail
in the list.
- If the list is empty, then assign the new node to the
- If the list is not empty:
- Assign the
next
property of the currenthead
to the new node. - Assign the new node to the
head
itself. - Increment the
length
by one.
- Assign the
unshift( value ) {
// create node
let node = new Node( value );
/** check if list is empty */
if( !this.head ) { // if empty
this.head = node;
this.tail = node;
} else {
node.next = this.head;
this.head = node
}
this.length++;
return this;
}
All this time we only added or removed nodes from either the start or the end of the linked list. For which we didn't have to do extra amount of work to find the position. If we want to add, delete or update nodes at a given position then it becomes a necessity to find positions of nodes in a Linked List.
findNode
In findNode
, we find a node at a given index
of the linked list.
Here are the steps which we are going to follow while coding findNode
.
- Create a method
findNode
in the class which is going to accept theindex
of the node which we want to find. - Checking whether the index is negative or out of bound. If so then return
null
. - We traverse through the linked list until we find the node at the given index, if found we return the
node
elsenull
.
findNode( index ){
if( index < 0 || index >= this.length ) {
return null;
}
let counter = 0,
node = this.head;
while( node ) {
if( counter === index ) {
// returns node at given index.
return node;
}
counter++;
node = node.next;
}
return null;
}
updateNode
In updateNode
, we find a node
at a given index
of the linked list and update the value of the node
.
Here are the steps which we are going to follow while coding updateNode
.
- Create a method
updateNode
in the class which is going to accept theindex
of the Linked List as an argument. - If index is negative or out of bound then return false.
- If not then:
- Storing the node of our desired
index
which we will find fromfindNode
function. - Assign the
data
which we want to update to the property calledvalue
of the node. - After updating the node we return.
- Storing the node of our desired
updateNode( data, index ) {
if( !this.findNode( index ) ) {
// returns false on negative index or out of bound.
return false;
} else {
let node = this.findNode( index );
node.value = data;
// updates node and returns true
return this;
}
}
deleteNode
In deleteNode
, we delete
a node
at any given index
of the linked list.
Here are the steps which we are going to follow while coding deleteNode
.
- Create a method
deleteNode
in the class which is going to accept theindex
of the Linked List as an argument. - If index is negative or out of bound then return.
- If the index is equal to zero then we will use
shift
function. - If not then:
- Storing the
node
of our desiredindex
which we will find fromfindNode
function. - After that we storing previous
node
using the samefindNode
function. - Assign the
next
of the previous node to thenext
of the node that we want to delete. - The
length
of the Linked List is decreased by one.
- Storing the
deleteNode( data, index ) {
if( !this.findNode( index ) ) {
// returns false on negative index or out of bound.
return false;
} else {
let node = this.findNode( index ),
prevNode = this.findNode( index - 1 );
prevNode.next = node.next;
this.length--;
// removes and returns node at given index
return this;
}
}
insert
In insert
function, we insert a node
at any given index
of the linked list.
Here are the steps which we are going to follow while coding insert
.
- Create a method
insert
in the class which is going to accept theindex
of the Linked List as an argument. - If index is negative or out of bound then return.
- If the index is equal to zero then we will use
unshift
function. - If not then:
- We store the
node
of our desiredindex
innextNode
variable and its previousnode
inprevNode
using thefindNode
function. - We create a new node where its
next
is the address of the node stored innextNode
and connect the newnode
by storing its address in thenext
of the node inprevNode
variable. - The
length
of the Linked List is increased by one.
- We store the
insert( data, index ) {
if( index < 0 || index >= this.length ){
// returns false if the index is negative or out of bound.
return false;
}
if( index === 0 ) {
this.unshift(data);
return this;
}
let prevNode = this.findNode( index - 1 ),
nextNode = this.findNode( index );
prevNode.next = new Node(data, nextNode);
this.length++;
// removes and returns node at given index
return this;
}
Conclusion ๐๏ธ
In this blog, we covered the fundamentals of Linked List. Its applications, advantages and disadvantages. We learned how to add or delete nodes to/from starting and ending of a linked list. We also learned how to find a node
in the Linked List at a given index
. And finally, we learned how to add, delete or update node
at a given index
.
Wrap Up ๐๏ธ
Thank you for reading.
If you find this helpful, leave a like, share, and follow me, @srafsan to read more articles.