Doubly Linked List Data Structure
Introduction to Doubly Linked List
A Doubly LinkedList is a variation of a linked list where the last node points back to the first node instead of null (or None in Python). This structure allows for a Doubly traversal where one can start from any node and eventually return to the same node. Doubly linked lists can be either singly or doubly linked.
Doubly LinkedList Operations
A Doubly LinkedList typically supports the following operations:
- Insertion Operations : Insertion at the beginning or end involves adjusting the next pointers to maintain the Doubly nature, and potentially updating the head pointer.
- At the Beginning
- At the End
- After a Given Node
- Deletion Operations : Deletion of nodes, whether from the beginning, end, or a specific node, requires properly updating the references of adjacent nodes so that the Doubly structure is maintained.
- Delete from the Beginning
- Delete from the End
- Delete by Key
-
Search Operation : The search operation traverses the list until it either finds the required node or returns to the head.
-
Traversal Operation : Traversal begins from the head and continues until the node just before the head is encountered again.
Pseudocode
Basic Operations
-
Insert at the Beginning:
function insertAtBeginning(list : DoublyLinkedList, value : DataType) {
newNode = new Node(value)
if list.head is null {
list.head = newNode
list.tail = newNode
} else {
newNode.next = list.head
list.head.prev = newNode
list.head = newNode
}
} -
Insert at the End:
function insertAtEnd(list : DoublyLinkedList, value : DataType) {
newNode = new Node(value)
if list.tail is null {
list.head = newNode
list.tail = newNode
} else {
list.tail.next = newNode
newNode.prev = list.tail
list.tail = newNode
}
} -
Insert after a given node:
function insertAfter(list : DoublyLinkedList, node : Node, value : DataType) {
if node is null {
return // Invalid node
}
newNode = new Node(value)
newNode.next = node.next
newNode.prev = node
if node.next is not null {
node.next.prev = newNode
}
node.next = newNode
if node is list.tail {
list.tail = newNode // Update tail if necessary
}
} -
Delete a Node:
function deleteNode(list : DoublyLinkedList, node : Node) {
if node is null {
return // Invalid node
}
if node.prev is not null {
node.prev.next = node.next
} else {
list.head = node.next // Update head if it's the first node
}
if node.next is not null {
node.next.prev = node.prev
} else {
list.tail = node.prev // Update tail if it's the last node
}
// Optional: Clear the node to help with garbage collection
node = null
} -
Traverse Forward:
function traverseForward(list : DoublyLinkedList) {
current = list.head
while current is not null {
print(current.value)
current = current.next
}
} -
Traverse Backward
function traverseBackward(list : DoublyLinkedList) {
current = list.tail
while current is not null {
print(current.value)
current = current.prev
}
}
Implementation in Python, C++, and Java
Python Implementation
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLinkedList:
def __init__(self):
self.head = None
def insert_at_beginning(self, data):
new_node = Node(data)
new_node.next = self.head
if self.head is not None:
self.head.prev = new_node
self.head = new_node
def insert_at_end(self, data):
new_node = Node(data)
if self.head is None:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
new_node.prev = last
def delete_node(self, key):
current = self.head
while current:
if current.data == key:
if current.prev:
current.prev.next = current.next
if current.next:
current.next.prev = current.prev
if current == self.head:
self.head = current.next
del current
return
current = current.next
print(f"Node with value {key} not found.")
def display(self):
current = self.head
while current:
print(current.data, end=" <=> ")
current = current.next
print("None")
# Example usage
if __name__ == "__main__":
dll = DoublyLinkedList()
dll.insert_at_end(10)
dll.insert_at_end(20)
dll.insert_at_beginning(5)
dll.display() # Output: 5 <=> 10 <=> 20 <=> None
dll.delete_node(10)
dll.display() # Output: 5 <=> 20 <=> None
C++ Implementation
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node* next;
Node* prev;
Node(int data) {
this->data = data;
this->next = nullptr;
this->prev = nullptr;
}
};
class DoublyLinkedList {
public:
Node* head;
DoublyLinkedList() {
head = nullptr;
}
void insertAtBeginning(int data) {
Node* newNode = new Node(data);
newNode->next = head;
if (head != nullptr) {
head->prev = newNode;
}
head = newNode;
}
void insertAtEnd(int data) {
Node* newNode = new Node(data);
if (head == nullptr) {
head = newNode;
return;
}
Node* last = head;
while (last->next != nullptr) {
last = last->next;
}
last->next = newNode;
newNode->prev = last; e
}
void deleteNode(int key) {
Node* current = head;
while (current != nullptr) {
if (current->data == key) {
if (current->prev != nullptr) {
current->prev->next = current->next;
}
if (current->next != nullptr) {
current->next->prev = current->prev;
}
if (current == head) {
head = current->next;
}
delete current;
return;
}
current = current->next;
}
cout << "Node with value " << key << " not found." << endl;
}
void display() {
Node* current = head;
while (current != nullptr) {
cout << current->data << " <=> ";
current = current->next;
}
cout << "None" << endl;
}
};
// Example usage
int main() {
DoublyLinkedList dll;
dll.insertAtEnd(10);
dll.insertAtEnd(20);
dll.insertAtBeginning(5);
dll.display(); // Output: 5 <=> 10 <=> 20 <=> None
dll.deleteNode(10);
dll.display(); // Output: 5 <=> 20 <=> None
return 0;
}
Java Implementation
class Node {
int data;
Node next;
Node prev;
public Node(int data) {
this.data = data;
this.next = null;
this.prev = null;
}
}
class DoublyLinkedList {
Node head;
public DoublyLinkedList() {
head = null;
}
public void insertAtBeginning(int data) {
Node newNode = new Node(data);
newNode.next = head;
if (head != null) {
head.prev = newNode;
}
head = newNode;
}
public void insertAtEnd(int data) {
Node newNode = new Node(data);
if (head == null) {
head = newNode;
return;
}
Node last = head;
while (last.next != null) {
last = last.next;
}
last.next = newNode;
newNode.prev = last;
}
public void deleteNode(int key) {
Node current = head;
while (current != null) {
if (current.data == key) {
if (current.prev != null) {
current.prev.next = current.next;
}
if (current.next != null) {
current.next.prev = current.prev;
}
if (current == head) {
head = current.next;
}
return;
}
current = current.next;
}
System.out.println("Node with value " + key + " not found.");
}
public void display() {
Node current = head;
while (current != null) {
System.out.print(current.data + " <=> ");
current = current.next;
}
System.out.println("None");
}
}
// Example usage
public class Main {
public static void main(String[] args) {
DoublyLinkedList dll = new DoublyLinkedList();
dll.insertAtEnd(10);
dll.insertAtEnd(20);
dll.insertAtBeginning(5);
dll.display(); // Output: 5 <=> 10 <=> 20 <=> None
dll.deleteNode(10);
dll.display(); // Output: 5 <=> 20 <=> None
}
}
Complexity
-
Time Complexity:
- Insertion :
- Deletion:
- Search:
- Traversal:
-
Space Complexity:
Conclusion
Doubly Linked Lists are powerful and versatile data structures that allow for efficient insertion and deletion from both ends and provides flexibility in traversing the list.