What is Doubly Linked List?
We already know what a linked list is, from the first section definition. A doubly linked list is by definition and function still the same as an SLL(singly linked lists) with the only difference being that a DLL(doubly linked list)has prev property attached to the node hence you can go back either forward or backwards.
For simplicity, I copied the code from the previous section and adjusted it to include the previous property. Also, the steps in each operation will need to be adjusted a little bit. Let's begin:-
Operations we to be Implemented
- push //add node a the end
- pop // remove node at the end
- shift // remove node at the beginning
- unshift // add a node at the beginning
- get // get node at a specific index or according to criteria
- set // change node value attribute
- insert //
- remove
- reverse // reverse the direction of the list
NB: Below I have done a deep dive into each function or method implementation. All the functions or methods are inside the class: Skip to the end to see the full code implementation then comeback and follow-through
Let's begin, ps: I will be doing the implementations in both Javascript and Python.
Push
In the push function, you will always be adding a node at the end of the list. The steps to follow are outlined below.
Step 1 - Create a newNode with given value and newNode → next as NULL.
Step 2 - Check whether list is Empty (head == NULL).
Step 3 - If it is Empty then, set head = newNode.
Step 4 - If it is Not Empty then point the tails next attribute to the newNode
step 5 - set the newNode's prev property to the current tail node
Step 6 - then reset the tail to point to the newNode and increment the size
code implementation in JavaScript:
class Node{
constructor(val){
this.val= val
this.prev = null
this.next=null
}
}
class DLL{
constructor(){
this.head= null
this.tail= null
this.size= 0
}
push(val){
let newNode= new Node(val);
if(!this.head){
this.head=newNode
this.tail= newNode
this.size++
return this
}
this.tail.next = newNode
newNode.prev =this.tail
this.tail = newNode
this.size++
return this
}
}
let list =new DLL()
list.push(20)
list.push(21)
list.push(22)
list.push(23)
In python:
class Node:
def __init__(self, val):
self.val = val
self.prev = None
self.next = None
class DLL:
def __init__(self):
self.head=None
self.tail= None
self.size=0
def traverse_list(self):
if(self.head is None):
print("No elements in this list")
return
else:
n = self.head
while n is not None:
print(n.val)
n = n.next
def push(self,val):
newNode = Node(val)
if(self.head == None):
self.head = newNode
self.tail = newNode
self.size+=1
return self
self.tail.next= newNode
newNode.prev = self.tail
self.tail = newNode
self.size+=1
return self
list = DLL()
list.push(20)
list.push(21)
list.push(22)
list.push(23)
list.traverse_list()
Pop
In the pop function, this involves always removing from the end. The steps to follow are as below
step 1 - Handle the edge case if the list is empty return None or undefined
step 2 - Create a temp and set it to the tail node
step 3 - If the size property is equal to 1 set the head and tail properties to null
step 4 - Set the tail attribute equal to the current tails prev attribute. Also, set the tail's next property to null and the temp variable's prev property to null.
step 5 - return the list
Code implementation in Javascript :
pop(){
if(!this.head) return undefined;
let temp = this.tail
if(this.size ===1){
this.head = null;
this.tail = null;
}else{
this.tail= this.tail.prev;
this.tail.next= null;
temp.prev = null
}
this.size--;
return this
}
In python:
def pop(self):
if self.head ==None:return
temp = self.tail
if self.size == 1:
self.head = None
self.tail = None
else:
self.tail = self.tail.prev
self.tail.next = None
temp.prev = None
self.size-=1
return self
Shift
This involves removing the first node in the list.
The steps to follow are below:
step 1: Check if the head exists otherwise return early
step 2: Initiate a variable called temp set it equal to the head property
step 3: If the size property is one the set the head and tail property to null or None
step 4: Otherwise set the head property to be the current heads next property
step 5: Set the current head's prev property to null
step 6: decrement the size by 1
step 6: Return the list
Code implementation in Javascript :
shift(){
if(!this.head) return undefined
let temp = this.head
if(this.size ===1){
this.head = null
this.tail =null
}else
this.head = this.head.next;
this.head.prev = null;
}
this.size --
return temp
}
In python:
def shift(self):
if self.head == None: return
temp = self.head
if(self.size == 1):
self.head = None
self.tail = None
else:
self.head = self.head.next
self.head.prev = None
self.size-=1
return temp
Unshift
From the name unshift
you can guess it is the opposite of shift and it involves adding a node a the beginning. Follow the steps below:
step 1: Create a variable newNode and instantiate the Node class will the val passed in.
step 2: If there is no head property set the head property and tail property equal to the newNode
step 3: if there is a head property set the newNodes's next property to the head property, set the head's prev property to the newNode and then set the head property to the newNode
step 4: Increment the size by 1 and return the list
Code implementation in Javascript :
unshift(val){
let newNode = new Node(val);
if(!this.head){
this.head= newNode;
this.tail = newNode;
}else{
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
this.size++;
return this;
}
In python:
def unshift(self,val):
newNode = Node(val)
if self.head == None:
self.head = newNode
self.tail = newNode
else:
newNode.next = self.head
self.head.prev = newNode
self.head = newNode
self.size+=1
return self
Get
Get method is just is a search criterion for a node it can use an index or value of the node but in this case, I will just use the index. This implementation is optimized to reduce the number of traversals by half. If the index is greater than half the size of the list we assume it is towards the end of the list, it makes more sense to start searching from the tail and vice versa if it is less than half the size. Follow the steps outline below:
step 1: If the index is less than 0 or greater than or equal to the size property return early
step 2: if index > (size/2) then we set the count to the size property minus one, set the current variable to tail node and start a while loop as long as count is not equal to index, setting current to the current's prev node and decrementing count by one each time
step 2: if index <= (size/2) then we set the count variable to zero, set the current variable to head property, and start a while loop as long as the count is not equal to index, setting current to the current's next node and incrementing count by one each time
step 4: when the loop breaks return current node
Code implementation in Javascript :
get(index){
if(index<0 || index >= this.size)return undefined;
if(index>Math.floor(this.size/2)){
let count=this.size -1;
let current= this.tail;
while(count !== index){
current= current.prev;
count--
}
}else{
let count =0;
let current = this.head
while(count !== index){
current= current.next;
count++
}
}
return current;
}
In python:
def get(self,index):
if index <0 or index >=self.size:return
if index > math.floor(self.size/2):
current= self.tail
count = self.size -1
while count != index:
current = current.next
count-=1
else:
current= self.head
count = 0
while count != index:
current = current.next
count+=1
return current
set
This method will use the Get method to find the node we want and set its value attribute to something else. Follow the steps outlined below:
step 1 : Create a node variable set it to the function
get
with index passed in as a parameter
step 2 : If the node variable is set to a Non-null or Non- None value set the val property of the node to the new val then return true otherwise return false
Code implementation in Javascript :
set(index, val){
let node = this.get(index);
if(node){
node.val = val;
return true;
}
return false;
}
In python:
def set(self,index,val):
node = self.get(index)
if node :
node.val = val
return True
return False
Insert
This method will insert a node at a specific point, it will also use the Get method as a helper method to determine where to insert the node. Follow the steps below:
Step 1: If the index parameter is less than zero or greater than the size property return early
step 2: if the index parameter is equal to zero, then return theunshift
method with the val parameter passed in
step 3: if the index is equal to the size property return thepush
method with the val parameter passed in
step 4: Create a variable newNode and instantiate the Node class will the value passed in.
step 5: Get the node at theindex -1
position
step 6: Create a nextNode's variable and set it equal to the current node's next property
step 7: set the current node's next property to the newNode and the newNodes's prev property to the node variable, set the newNode's next property to the nextNode and the nextNodes's prev property to the newNode
step 8: Increment the size property by 1 and return the list
Code implementation in Javascript :
insert(index, val){
if(index<0 || index > this.size ) return undefined
if(index === 0){
this.unshift(val);
}else if(index === this.size){
this.push(val);
}else{
let newNode = new Node(val);
let node = this.get(index-1);
let nextNode = node.next;
node.next = newNode, newNode.prev = node;
newNode.next = nextNode, nextNode.prev = newNode;
}
this.size++;
return this;
}
In python:
def insert(self,index, val):
if index<0 or index> self.size: return
if index == 0: return self.unshift(val)
if index == self.size: return self.push(val)
newNode = Node(val)
prevNode = self.get(index-1)
nextNode = prevNode.next
prevNode.next = newNode
newNode.prev= prevNode
newNode.next = nextNode
nextNode.prev = newNode
self.size+=1
return self
Remove
This method removes an element from the list.The steps to follow are outlined below:
Step 1: If the index parameter is less than zero or greater than or equal to the size property return early
step 2: if the index parameter is equal to zero, the return theshift
shift method.
step 3: if the index is equal to the size property minus
1 return thepop
method.
step 4: Create a variable prevNode and set it to the results ofget
method withindex-1
.
step 5: Get the node at theindex -1
position,
step 6: Create a temp variable and set it equal to the prevNode's next property, create another afterNode and set it to be the temp's variable next property, then set the prevNode's next property to be the afterNode, set the afterNode prev's property to be the prevNode, set hte temp's next and temp's prev property to null
step 7: decrement the size property by 1 and return the list
Code implementation in Javascript :
remove(index){
if(index<0 || index >= this.size ) return undefined
if(index === 0) return this.shift()
if(index === this.size-1) return this.pop()
let prevNode = this.get(index-1)
let temp = prevNode.next
let afterNode = temp.next
prevNode.next = afterNode
afterNode.prev = prevNode
temp.next = null
temp.prev = null
this.size--
return this
}
In python:
def remove(self,index):
if index<0 or index>= self.size: return
if index == 0:
return self.shift()
if index == self.size-1:
return self.pop()
prevNode = self.get(index-1)
temp = prevNode.next
afterNode = temp.next
prevNode.next = afterNode
afterNode.prev = prevNode
temp.next = None
temp.prev = None
self.size-=1
return self
Final Code solution for JavaScript :
class Node{
constructor(val){
this.val= val
this.prev = null
this.next=null
}
}
class DLL{
constructor(){
this.head= null
this.tail= null
this.size= 0
}
push(val){
let newNode= new Node(val);
if(!this.head){
this.head=newNode
this.tail= newNode
this.size++
return this
}
this.tail.next = newNode
newNode.prev =this.tail
this.tail = newNode
this.size++
return this
}
pop(){
if(!this.head) return undefined;
let temp = this.tail
if(this.size ===1){
this.head = null;
this.tail = null;
}else{
this.tail=this.tail.prev;
this.tail.next = null;
temp.prev= null;
}
this.size--;
return this;
}
//shift
shift(){
if(!this.head) return undefined
let temp = this.head;
if(this.size ===1){
this.head = null
this.tail =null;
}else{
this.head = this.head.next;
this.head.prev = null
}
this.size --;
return temp
}
//unshift
unshift(val){
let newNode = new Node(val);
if(!this.head){
this.head= newNode;
this.tail = newNode;
}else{
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
this.size++;
return this;
}
//get
get(index){
if(index<0 || index >= this.size)return undefined;
let current;
if(index >Math.floor(this.size/2)){
let count=this.size-1;
current= this.tail;
while(count !== index){
current= current.prev;
count--
}
}else{
let count=0;
current= this.head;
while(count !== index){
current= current.next;
count++
}
}
return current;
}
//set
set(index, val){
let node = this.get(index);
if(node){
node.val = val;
return true;
}
return false;
}
//insert
insert(index, val){
if(index<0 || index > this.size ) return undefined
if(index === 0){
this.unshift(val);
}else if(index === this.size){
this.push(val);
}else{
let newNode = new Node(val);
let node = this.get(index -1);
let nextNode = node.next;
node.next = newNode, newNode.prev = node;
newNode.next = nextNode, nextNode.prev = newNode
}
this.size++;
return this;
}
//remove
remove(index){
if(index<0 || index >= this.size ) return undefined
if(index === 0) return this.shift()
if(index === this.size-1) return this.pop()
let prevNode = this.get(index-1)
let temp = prevNode.next
let afterNode = temp.next
prevNode.next = afterNode
afterNode.prev = prevNode
temp.next = null
temp.prev = null
this.size--
return temp
}
//reverse
//print
print(){
let current= this.head
let arr = []
while(current){
arr.push(current.val)
current = current.next
}
return arr
}
}
let list =new DLL()
list.push(20)
list.push(21)
list.push(22)
list.push(23)
for Python:
import math
class Node:
def __init__(self, val):
self.val = val
self.prev = None
self.next = None
class DLL:
def __init__(self):
self.head=None
self.tail= None
self.size=0
def traverse_list(self):
if(self.head is None):
print("No elements in this list")
return
else:
n = self.head
while n is not None:
print(n.val)
n = n.next
def push(self,val):
newNode = Node(val)
if(self.head == None):
self.head = newNode
self.tail = newNode
self.size+=1
return self
self.tail.next= newNode
newNode.prev = self.tail
self.tail = newNode
self.size+=1
return self
def pop(self):
if self.head ==None:return
temp = self.tail
if self.size == 1:
self.head = None
self.tail = None
else:
self.tail = self.tail.prev
self.tail.next = None
temp.prev = None
self.size-=1
return self
def shift(self):
if self.head == None: return
temp = self.head
if(self.size == 1):
self.head = None
self.tail = None
else:
self.head = self.head.next
self.head.prev = None
self.size-=1
return temp
def unshift(self,val):
newNode = Node(val)
if self.head == None:
self.head = newNode
self.tail = newNode
else:
newNode.next = self.head
self.head.prev = newNode
self.head = newNode
self.size+=1
return self
def get(self,index):
if index <0 or index >=self.size:return
if index > math.floor(self.size/2):
current= self.tail
count = self.size -1
while count != index:
current = current.next
count-=1
else:
current= self.head
count = 0
while count != index:
current = current.next
count+=1
return current
def set(self,index,val):
node = self.get(index)
if node :
node.val = val
return True
return False
def insert(self,index, val):
if index<0 or index> self.size: return
if index == 0: return self.unshift(val)
if index == self.size: return self.push(val)
newNode = Node(val)
prevNode = self.get(index-1)
nextNode = prevNode.next
prevNode.next = newNode
newNode.prev= prevNode
newNode.next = nextNode
nextNode.prev = newNode
self.size+=1
return self
def remove(self,index):
if index<0 or index>= self.size: return
if index == 0:
return self.shift()
if index == self.size-1:
return self.pop()
prevNode = self.get(index-1)
temp = prevNode.next
afterNode = temp.next
prevNode.next = afterNode
afterNode.prev = prevNode
temp.next = None
temp.prev = None
self.size-=1
return self
list = DLL()
list.push(20)
list.push(21)
list.push(22)
list.push(23)
list.traverse_list()
print("==============")
list.remove(2)
print("==============")
print("==============")
list.traverse_list()
print("==============")
As you can observe the final solution bears some similarity to the SLL solution with some small differences.
Advantages Of DLL:
- Reversing the doubly linked list is very easy.
- It can allocate or reallocate memory easily during its execution.
- As with a singly linked list, it is the easiest data structure to implement.
- The traversal of this doubly linked list is bidirectional which is not possible in a singly linked list.
- Deletion of nodes is easy as compared to a Singly Linked List. A singly linked list deletion requires a pointer to the node and previous node to be deleted but in the doubly linked list, it only required the pointer which is to be deleted.
Disadvantages Of DLL:
- It uses extra memory when compared to the array and singly linked list.
- Since elements in memory are stored randomly, therefore the elements are accessed sequentially no direct access is allowed.
Conclusion
You can look up this article for more information on doubly linked lists and their uses. Next in this series, We will take a look at implementing stack and Queues using Linked lists.
Top comments (0)