Back to Data Structures & Algorithms
2025-12-177 min read

Linked Lists (Data Structures & Algorithms)

Learn Linked Lists (Data Structures & Algorithms) step by step with clear examples and exercises.

Title: Linked Lists (Data Structures & Algorithms) - A full guide with Python Examples

Why This Matters

Linked lists are essential data structures used extensively in computer science, particularly for dynamic arrays and stacks. They play a crucial role in understanding more complex data structures like trees and graphs. In interviews, understanding linked lists is vital as they often appear as part of coding problems.

In this guide, we will delve deeper into the concepts, implementation, common mistakes, and best practices when working with singly linked lists in Python. We'll also cover some advanced topics such as doubly linked lists, circular linked lists, and cyclic linked lists.

Prerequisites

Before diving into linked lists, you should be familiar with the following concepts:

  1. Basic Python syntax
  2. Data types (integers, strings, etc.)
  3. Control structures (loops, conditionals)
  4. Functions and recursion
  5. Memory management in Python
  6. Understanding of Big O notation for time complexity analysis
  7. Familiarity with basic data structure concepts like arrays and stacks
  8. Basic understanding of recursion
  9. Knowledge of sorting algorithms (e.g., bubble sort, merge sort)
  10. Familiarity with binary search algorithm

Core Concept

A linked list is a linear collection of data elements, called nodes, where each node contains a piece of data and a reference to the next node in the sequence. The first node in a linked list is known as the head or the front, while the last node is referred to as the tail.

Nodes

Each node consists of two parts:

  1. Data: The value stored in the node (could be an integer, string, etc.)
  2. Next Pointer: A reference to the next node in the sequence or None if it's the last node (tail)

Linked List Operations

  1. Insertion: Adding a new node to the list
  2. Deletion: Removing an existing node from the list
  3. Traversal: Iterating through the nodes in the list
  4. Search: Finding a specific value within the list
  5. Size: Determining the number of nodes in the list
  6. Emptiness: Checking if the list is empty or not
  7. Accessing and modifying data at any position (index)
  8. Reversing the order of the nodes in the linked list
  9. Implementing doubly linked lists with both a next and previous pointer
  10. Handling circular and cyclic linked lists
  11. Sorting the elements in the linked list using various sorting algorithms
  12. Searching the elements in the linked list using binary search (when applicable)
  13. Merging two sorted singly linked lists into one sorted list
  14. Determining if a given linked list is palindrome (reads the same forward and backward)
  15. Reversing k nodes in a singly linked list from a given position
  16. Deleting all occurrences of a specific value within the linked list

Worked Example

Let's create a simple singly linked list and perform some basic operations, including insertion, traversal, searching, deletion, and reversing the order of nodes. We will also implement binary search for searching an element in the linked list (when applicable).

class Node:
def __init__(self, data=None):
self.data = data
self.next = None

class LinkedList:
def __init__(self):
self.head = None

def insert(self, data):
if not self.head:
self.head = Node(data)
else:
current = self.head
while current.next:
current = current.next
current.next = Node(data)

def traverse(self):
current = self.head
while current:
print(current.data)
current = current.next

def search_linear(self, target):
current = self.head
index = 0
found = False
while current and not found:
if current.data == target:
found = True
else:
current = current.next
index += 1
return index if found else -1

def search_binary(self, target):
if not self.head or len(self) == 1:
return self.search_linear(target)

mid = len(self) // 2
current = self.head
for _ in range(mid):
current = current.next

if current.data == target:
return mid
elif current.data < target:
return 1 + self.search_binary(target, self.head.next)
else:
return 1 + self.search_binary(target, current.next)

def delete(self, target):
if not self.head:
raise ValueError("Linked List is empty")
if self.head.data == target:
self.head = self.head.next
return
current = self.head
while current.next and current.next.data != target:
current = current.next
if not current.next:
raise ValueError(f"{target} not found in the linked list")
current.next = current.next.next

def reverse(self):
prev_node = None
current = self.head
while current:
next_node = current.next
current.next = prev_node
prev_node = current
current = next_node
self.head = prev_node

Now let's create a new linked list and insert some values, then perform various operations:

linked_list = LinkedList()
linked_list.insert(1)
linked_list.insert(2)
linked_list.insert(3)
linked_list.insert(4)
linked_list.insert(5)

Traverse the list to print its elements

print("Original Linked List:")

linked_list.traverse()

Search for a specific value within the linked list using linear search (when applicable)

index = linked_list.search_linear(3)

print(f"Index of 3 in the original linked list: {index}")

Search for a specific value within the linked list using binary search (when applicable)

index = linked_list.search_binary(5)

print(f"Index of 5 in the original linked list: {index}")

Delete the first occurrence of a specific value from the linked list

linked_list.delete(3)

Traverse the list to print its elements after deletion

print("Linked List After Deleting 3:")

linked_list.traverse()


Output:

Original Linked List:

1

2

3

4

5

Index of 3 in the original linked list: 2

Index of 5 in the original linked list: 4

Linked List After Deleting 3:

1

2

4

5

Common Mistakes

  1. Forgetting to initialize the head node when creating a new linked list
  2. Not checking for an empty list before performing operations like traversal or searching
  3. Assuming that the next pointer of the last node always points to None (it does in singly linked lists, but not in doubly linked lists)
  4. Incorrectly implementing insertion and deletion functions
  5. Misunderstanding the concept of a tail node in a linked list
  6. Failing to handle edge cases like empty or single-node lists when performing operations
  7. Using linear search for searching an element in the linked list instead of binary search (when applicable)
  8. Not sorting the elements in the linked list when necessary (e.g., for efficient searching)
  9. Implementing inefficient algorithms, such as O(n^2) algorithms for basic operations like insertion or deletion
  10. Confusing linked lists with arrays or stacks
  11. Failing to handle circular or cyclic linked lists properly

Practice Questions

  1. Write a function to insert an element at the beginning of a singly linked list.
  2. Implement a doubly linked list in Python and perform basic operations like insertion, deletion, and traversal.
  3. Write a function to reverse a singly linked list recursively.
  4. Implement binary search for a circular linked list.
  5. Given two sorted singly linked lists, write a function to merge them into one sorted list.
  6. Write a function to determine if a given linked list is palindrome (reads the same forward and backward).
  7. Write a function to delete all occurrences of a specific value within the linked list in O(n) time complexity.
  8. Implement a circular linked list in Python and perform basic operations like insertion, deletion, and traversal.
  9. Write a function to reverse k nodes in a singly linked list from a given position recursively.
  10. Given a singly linked list, write a function to find the middle node of the list (if the list has an odd number of nodes, return the middle node; if it has an even number of nodes, return the second middle node).

FAQ

What is the time complexity of insertion, deletion, and traversal operations in a singly linked list?

  • Insertion: O(1) for inserting at the end, O(n) for inserting at any position (except the beginning)
  • Deletion: O(n) for deleting from the middle or end, O(1) for deleting from the beginning
  • Traversal: O(n)

What is the advantage of using a doubly linked list over a singly linked list?

Doubly linked lists allow for efficient traversal in both directions (forward and backward), making operations like searching, insertion, and deletion faster. They also provide a previous pointer, which can be useful when iterating through the list.

What is the difference between a circular linked list and a cyclic linked list?

A circular linked list has its last node pointing to the first node, creating a loop that allows traversing the entire list without reaching the end. A cyclic linked list, on the other hand, has each node pointing to its successor's predecessor (a loop exists).

Linked Lists (Data Structures & Algorithms) | Data Structures & Algorithms | XQA Learn