In this chapter you will learn two core data structures: arrays (Python lists) and linked lists. These are the foundation of many other structures (stacks, queues, hash tables, etc.).
What you will learn
An array is a fundamental data structure that stores a collection of elements
(values) in a contiguous block of memory.
In Python, the built-in list type behaves like a dynamic array.
An array is a sequence of elements stored in contiguous memory. This allows very fast access by index.
Python does not expose low-level C-style arrays directly, but its built-in
list type behaves like a dynamic array.
Python's list type is a dynamic array that can grow and shrink in size.
It supports fast index access and amortized O(1) appends.
Here are the average time complexities for common array (list) operations in Python:
| Operation | Average Time |
|---|---|
Access by index arr[i] | O(1) |
| Append at end | O(1) amortized |
| Insert at index | O(n) |
| Delete / remove | O(n) |
| Search (unsorted) | O(n) |
array Module
For memory-efficient storage of numbers, Python provides the array module
(all elements have the same fixed type).
Use arrays (Python lists) when:
A linked list is a linear data structure where elements are stored in nodes, and each node contains a value and a reference (pointer) to the next node. Memory is not contiguous.
Each node has:
Advantages:
Disadvantages:
Each node contains data and a pointer to the next node.
class Node:
def __init__(self, data):
self.data = data
self.next = None
The LinkedList class manages the nodes and provides methods to manipulate the list.
Example usage of the LinkedList class:
ll = LinkedList()
ll.insert_at_front(10) # [10]
ll.insert_at_front(5) # [5, 10]
ll.insert_at_end(20) # [5, 10, 20]
print(ll.to_list()) # [5, 10, 20]
print(ll.search(10)) # True
print(ll.search(99)) # False
ll.delete(10) # [5, 20]
print(ll.to_list())
Here are the average time complexities for common linked list operations:
| Operation | Time |
|---|---|
| Insert at head | O(1) |
| Insert at end (no tail pointer) | O(n) |
| Delete head | O(1) |
| Delete by value (search + unlink) | O(n) |
| Search by value | O(n) |
| Access by index | O(n) |
We usually do not use linked lists when we need random access by index. They shine when we care about frequent insertions and deletions.
Each node points only to the next node.
Each node points to both previous and next nodes.
class DNode:
def __init__(self, data):
self.data = data
self.prev = None
self.next = None
Python's collections.deque is implemented as a doubly-linked list,
optimized for fast appends and pops from both ends.
In a circular linked list, the last node points back to the first node, forming a circle.
Useful for round-robin scheduling or cyclic buffers.
Here is a side-by-side comparison of arrays (Python lists) and linked lists:
| Feature | Array (Python list) | Linked List |
|---|---|---|
| Memory layout | Contiguous block | Scattered nodes with pointers |
| Access by index | O(1) | O(n) (must traverse) |
| Insert at head | O(n) (shift elements) | O(1) |
| Insert in middle | O(n) | O(n) (find node, then link) |
| Delete at head | O(n) | O(1) |
| Memory overhead | Lower (only values) | Higher (values + pointers) |
| Cache friendliness | High (contiguous) | Low (jumps in memory) |
| Typical uses | Random access, numeric arrays, sorting | Queues, stacks, LRU cache, playlists |
Here are guidelines on when to choose arrays (lists) or linked lists:
arr[i]You need frequent insertions and deletions without shifting:
Here are some classic coding problems involving arrays and linked lists to practice your skills:
Here are some common linked list problems to solve:
These are very common in coding interviews and are excellent practice for mastering arrays and linked lists.