Linked Lists
In computer science, a linked list is a linear data structure used to store a collection of elements, called nodes, where each node points to the next one. Linked lists are a fundamental data structure that provides efficient insertion and deletion operations compared to arrays.
A linked list is a linear collection of nodes, where each node contains two parts:
In a linked list, the elements are not stored in contiguous memory locations like arrays. Instead, each element is dynamically allocated, and each node points to the next node in the list.
The key advantage of linked lists over arrays is that they allow for dynamic memory allocation. Linked lists can easily grow or shrink in size, unlike arrays that have a fixed size once they are created.
There are several variations of linked lists, each serving a different purpose. Let's explore the most common types:
In a singly linked list, each node contains a data field and a reference to the next node in the sequence. The last node in the list has a reference (or link) to null
(or None
in Python), indicating the end of the list.
Structure:
A doubly linked list is an extension of the singly linked list where each node contains two references:
This allows traversal in both directions, making it more versatile than a singly linked list.
Structure:
In a circular linked list, the last node points back to the first node, forming a loop. There are two types:
The following operations are commonly performed on linked lists:
Insertion:
Deletion:
Traversal: Traverse the list and print all the elements.
Search: Find a node by value.
Let's implement a basic singly linked list in Python. This implementation will include methods for insertion, deletion, and traversal.
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
# Insert at the beginning
def insert_at_beginning(self, data):
new_node = Node(data)
new_node.next = self.head
self.head = new_node
# Insert at the end
def insert_at_end(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last_node = self.head
while last_node.next:
last_node = last_node.next
last_node.next = new_node
# Delete a node by value
def delete_node(self, key):
temp = self.head
# If the node to be deleted is the head node
if temp and temp.data == key:
self.head = temp.next
temp = None
return
# Search for the node to be deleted
prev = None
while temp and temp.data != key:
prev = temp
temp = temp.next
# If the node was not found
if not temp:
return
# Unlink the node from the linked list
prev.next = temp.next
temp = None
# Traverse the list and print all elements
def traverse(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# Example usage
ll = LinkedList()
ll.insert_at_beginning(10)
ll.insert_at_beginning(20)
ll.insert_at_end(30)
ll.insert_at_end(40)
print("Linked List:")
ll.traverse() # Output: 20 -> 10 -> 30 -> 40 -> None
ll.delete_node(10)
print("After Deletion:")
ll.traverse() # Output: 20 -> 30 -> 40 -> None
Here’s a simple implementation of a doubly linked list in Python, which supports both forward and backward traversal.
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLinkedList:
def __init__(self):
self.head = None
# Insert at the beginning
def insert_at_beginning(self, data):
new_node = Node(data)
if self.head:
self.head.prev = new_node
new_node.next = self.head
self.head = new_node
# Insert at the end
def insert_at_end(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last_node = self.head
while last_node.next:
last_node = last_node.next
last_node.next = new_node
new_node.prev = last_node
# Traverse forward
def traverse_forward(self):
current = self.head
while current:
print(current.data, end=" <-> ")
current = current.next
print("None")
# Traverse backward
def traverse_backward(self):
current = self.head
while current and current.next:
current = current.next
while current:
print(current.data, end=" <-> ")
current = current.prev
print("None")
# Example usage
dll = DoublyLinkedList()
dll.insert_at_beginning(10)
dll.insert_at_beginning(20)
dll.insert_at_end(30)
dll.insert_at_end(40)
print("Doubly Linked List (Forward Traversal):")
dll.traverse_forward() # Output: 20 <-> 10 <-> 30 <-> 40 <-> None
print("Doubly Linked List (Backward Traversal):")
dll.traverse_backward() # Output: 40 <-> 30 <-> 10 <-> 20 <-> None