1. Home
Data Structure

Data Structure Tutorial: Everything You Need to Know

Learn all about data structures with our comprehensive tutorial. Master the fundamentals and advance your skills in organizing and managing data efficiently.

  • 60
  • 14
right-top-arrow

Tutorial Playlist

58 Lessons
6

Circular doubly linked list

Updated on 25/07/2024439 Views

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.

What is circular doubly linked list?

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.

Implementation of circular doubly linked list

The circular doubly linked list implementation involves carefully threading nodes together.

Node structure

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.

Creating a circular doubly linked list

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

Insertion operations can be performed at various positions in the list:

  • Insert at the beginning:

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.

  • Insert at the end: (Handled in the append method)

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.

  • Insert at a specific position:

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

Deletion operations can also be performed at various positions in the list:

  • Delete at the beginning:

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.

  • Delete at the end:

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.

  • Delete at a specific position:

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).

Traversal Operations

  • Time complexity: O(n)
  • Explanation: Traversing the list to visit each node once involves a single pass through all n nodes.

Edge cases handling

Operations on an empty list

  • Insertion: When inserting into an empty list, the new node must point to itself in both the next and previous pointers. The head and tail pointers are updated to reference this new node.
  • Deletion: Depending on implementation specifics, attempting to delete from an empty list should throw an exception or do nothing.

Operations on a single-node list

  • Insertion: When inserting a new node into a list with one node, the existing node's next and prev pointers are updated to point to the new node, and vice versa.
  • Deletion: Deleting the single node in the list requires setting the head and tail pointers to null, effectively making the list empty.

Memory management techniques and considerations

Efficient memory management in circular doubly linked lists involves proper node allocation/deallocation and handling circular references to avoid memory leaks.

1. Node creation and deletion


  • Memory allocation: Each node requires allocation for its data and two pointers (next and prev). Efficient memory allocation strategies, such as object pools, can help manage memory more effectively, especially in high-performance scenarios.
  • Garbage collection: In languages with automatic garbage collection, ensure that nodes are properly dereferenced so they can be collected. In manual memory management environments, explicitly deallocate nodes to prevent memory leaks.

2. Circular reference handling

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.

3. Efficient pointer updates

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.

Applications of circular doubly linked lists

Circular doubly linked lists find diverse applications across various domains:

Usage in data structures

Circular doubly linked lists find applications in various data structures due to their unique properties, including:

  • Circular queues: Circular queues utilize circular doubly linked lists to efficiently manage elements in a first-in-first-out (FIFO) manner. The circular structure allows for continuous rotation of elements without the need for resizing the underlying array, making it suitable for scenarios with fixed-size buffers or queues.
  • Circular buffers: Circular buffers, commonly used in embedded systems and real-time applications, use circular doubly linked lists to store and manage data in a circular manner. This enables continuous data processing with efficient memory utilization and minimal overhead.

Real-world applications

Circular doubly linked lists are also employed in various real-world applications, including:

  • Media players: Circular doubly linked lists can be used to implement playlist functionalities in media players. Each node in the list represents a media file, and the circular structure allows efficient navigation between different media files in both forward and backward directions.
  • Resource management systems: In systems where resources need to be allocated and deallocated dynamically, circular, doubly linked lists can be utilized to manage resource pools efficiently. For example, in memory management systems, the circular structure facilitates the allocation and deallocation of memory blocks while minimizing fragmentation.

Advantages of circular doubly linked list

The circular doubly linked list offers advantages that make it a versatile and powerful data structure.

  1. Efficient traversal: Circular doubly linked lists enable swift traversal in both forward and backward directions. With each node holding references to its previous and next nodes, navigation through the list becomes easy.
  1. Efficient insertion and deletion operations: Inserting and deleting nodes in circular doubly linked lists is efficient. The interconnectedness of the head and tail nodes due to the circular structure simplifies addition or removal from either end without the need to traverse the entire list.
  1. Integral to circular data structures: Circular doubly linked lists are essential for implementing circular data structures like circular queues and buffers. Their ability to manage data circularly is instrumental in these contexts.
  1. Efficient utilization of memory: Circular doubly linked lists efficiently utilize memory due to their ability to reuse the last node's reference for the first node, forming a circular structure. This minimizes memory fragmentation and improves memory usage efficiency compared to other data structures.

Example code

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.

Conclusion

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.

FAQs

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.

Rohan Vats

Rohan Vats

Passionate about building large scale web apps with delightful experiences. In pursuit of transforming engineers into leaders.

Get Free Career Counselling
form image
+91
*
By clicking, I accept theT&Cand
Privacy Policy
image
right-top-arrowleft-top-arrow

upGrad Learner Support

Talk to our experts. We’re available 24/7.

text

Indian Nationals

1800 210 2020

text

Foreign Nationals

+918045604032

Disclaimer

upGrad does not grant credit; credits are granted, accepted or transferred at the sole discretion of the relevant educational institution offering the diploma or degree. We advise you to enquire further regarding the suitability of this program for your academic, professional requirements and job prospects before enr...