For working professionals
For fresh graduates
More
14. Radix Sort
20. AVL Tree
21. Red-Black Tree
23. Expression Tree
24. Adjacency Matrix
36. Greedy Algorithm
42. Tower of Hanoi
43. Stack vs Heap
47. Fibonacci Heap
49. Sparse Matrix
50. Splay Tree
I clearly remember an intense coding competition in which I ran into an issue that at first looked impossible. The assignment? Effectively modify a dataset for real-time analysis. I had a breakthrough and realized that the secret to solving the problem might lie in a circular, doubly linked list. The structure's elegance amazed me as I methodically threaded nodes together, getting into the implementation. It was a revelation: bidirectional traversal and smooth data manipulation. Circular doubly linked lists were my first choice for situations needing effective navigation and dynamic data management after that.
Circular doubly linked lists are advanced data structures characterized by nodes that hold references to both their previous and next nodes. Unlike singly linked lists, circular doubly linked lists do not contain NULL pointers, creating a circular arrangement where the last node points back to the first, and the first node points to the last.
The structure of a circular doubly linked list consists of three main components per node: the data, a pointer to the previous node, and a pointer to the next node. This design allows for efficient traversal in both forward and backward directions, as nodes can be easily accessed from any point in the list.
The circular doubly linked list implementation involves carefully threading nodes together.
class Node:
def __init__(self, data):
self.data = data
self.prev = None
self.next = None
The Node class represents individual nodes of the circular doubly linked list, where every node contains data and pointers to the previous and next nodes.
class CircularDoublyLinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
new_node.next = new_node
new_node.prev = new_node
else:
last_node = self.head.prev
last_node.next = new_node
new_node.prev = last_node
new_node.next = self.head
self.head.prev = new_node
The Circular Doubly Linked List class manages the circular doubly linked list. The append technique adds a new node to the end of the list. If the list is null, then the new node becomes the head and points to itself.
Insertion operations can be performed at various positions in the list:
def insert_at_beginning(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
new_node.next = new_node
new_node.prev = new_node
else:
last_node = self.head.prev
last_node.next = new_node
new_node.prev = last_node
new_node.next = self.head
self.head.prev = new_node
self.head = new_node
Time complexity: O(1)
Explanation: Inserting at the beginning involves creating a new node and adjusting a few pointers (the new node's next and prev, and the current head and tail nodes' next and prev). This is a constant time operation.
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
new_node.next = new_node
new_node.prev = new_node
else:
last_node = self.head.prev
last_node.next = new_node
new_node.prev = last_node
new_node.next = self.head
self.head.prev = new_node
Time complexity: O(1)
Explanation: Inserting at the end, like at the beginning, involves creating a new node and updating the pointers of the tail and head nodes. This operation is also constant time.
def insert_at_position(self, data, position):
if position == 0:
self.insert_at_beginning(data)
else:
new_node = Node(data)
current = self.head
for _ in range(position - 1):
current = current.next
new_node.next = current.next
new_node.prev = current
current.next.prev = new_node
current.next = new_node
Time complexity: O(n)
Explanation: Inserting at a specific position requires traversing the list to that position, which in the worst case involves n steps (where n is the number of nodes). Once at the position, the insertion is O(1).
Deletion operations can also be performed at various positions in the list:
def delete_at_beginning(self):
if not self.head:
return
if self.head.next == self.head:
self.head = None
else:
last_node = self.head.prev
self.head = self.head.next
last_node.next = self.head
self.head.prev = last_node
Time complexity: O(1)
Explanation: Deleting the first node involves updating the head pointer and the previous head node's pointers. If the list becomes empty, additional handling is required to set the head and tail to null.
def delete_at_end(self):
if not self.head:
return
if self.head.next == self.head:
self.head = None
else:
last_node = self.head.prev
last_node.prev.next = self.head
self.head.prev = last_node.prev
Time complexity: O(1)
Explanation: Deleting the last node involves updating the tail pointer and the previous tail node's pointers. Similar to deletion at the beginning, if the list becomes empty, the head and tail need to be set to null.
def delete_at_position(self, position):
if not self.head:
return
if position == 0:
self.delete_at_beginning()
else:
current = self.head
for _ in range(position):
current = current.next
current.prev.next = current.next
current.next.prev = current.prev
Time complexity: O(n)
Explanation: Deleting a node at a specific position requires traversing the list to that position (O(n) time) and then adjusting the pointers of the neighboring nodes (O(1) time).
Efficient memory management in circular doubly linked lists involves proper node allocation/deallocation and handling circular references to avoid memory leaks.
Circular doubly linked lists inherently have circular references, which can be problematic for garbage collection in some languages. Care must be taken to break these references when nodes are deleted, or the list is destroyed.
When modifying the list (inserting or deleting nodes), minimize the number of pointer updates required. Keeping track of the head and tail pointers ensures operations at the ends of the list remain efficient.
Circular doubly linked lists find diverse applications across various domains:
Circular doubly linked lists find applications in various data structures due to their unique properties, including:
Circular doubly linked lists are also employed in various real-world applications, including:
The circular doubly linked list offers advantages that make it a versatile and powerful data structure.
Here's a simple example code demonstrating the implementation of a circular doubly linked list:
class Node:
def __init__(self, data):
self.data = data
self.prev = None
self.next = None
class CircularDoublyLinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
new_node.next = new_node
new_node.prev = new_node
else:
last_node = self.head.prev
last_node.next = new_node
new_node.prev = last_node
new_node.next = self.head
self.head.prev = new_node
def display(self):
if not self.head:
print("List is empty")
return
current = self.head
while True:
print(current.data, end=" ")
current = current.next
if current == self.head:
break
print("->", end=" ")
# Example usage
if __name__ == "__main__":
cdl = CircularDoublyLinkedList()
cdl.append(1)
cdl.append(2)
cdl.append(3)
cdl.append(4)
print("Circular Doubly Linked List:")
cdl.display()
This code defines a Node class to represent individual nodes of the circular doubly linked list and a CircularDoublyLinkedList class to manage the list. The append technique adds a new node at the end of the list, and the display technique prints the elements of the list.
The circular doubly linked list is an excellent example of how sophisticated data structures can be both elegant and effective. Its special circular structure and bidirectional traversal capabilities enable dynamic data management and smooth navigation. This structure has invaluable uses in many disciplines, from computer science to media players, despite its increased complexity and memory overhead.
1. What is an example of a doubly linked list?
A list where each node contains a reference to both its previous and next nodes, allowing bidirectional traversal.
2. Is the circular linked list single or double?
A circular linked list is neither single nor double; it's a distinct type where the last node points back to the first, forming a circular structure.
3. How is a circular linked list different?
Unlike linear linked lists, a circular linked list doesn't have a distinct end; instead, its last node points back to the first, creating a continuous loop.
4. What is the circular doubly linked list?
The circular doubly linked list is a variation of the doubly linked list where the last node points to the first, and the first node points to the last, forming a circular structure.
5. What is the difference between DLL and DCLL?
The main difference between DLL (Doubly Linked List) and DCLL (Circular Doubly Linked List) is that DCLL's last node points to the first, creating a circular structure.
6. What is a circular linked list in detail?
It is a linked list where the last node directs back to the first, forming a circular structure. It's useful for applications requiring continuous data processing or looping navigation.
Author
Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)
Indian Nationals
1800 210 2020
Foreign Nationals
+918045604032
1.The above statistics depend on various factors and individual results may vary. Past performance is no guarantee of future results.
2.The student assumes full responsibility for all expenses associated with visas, travel, & related costs. upGrad does not provide any a.