04
Essential Data Structures
Chapter 4 • Intermediate
150 min
Essential Data Structures
Overview
Data structures are ways of organizing and storing data to enable efficient operations. Choosing the right data structure is crucial for algorithm efficiency.
Arrays
Properties
- Access: O(1) - direct indexing
- Search: O(n) - linear search
- Insertion: O(n) - shift elements
- Deletion: O(n) - shift elements
- Space: O(n)
Use Cases
- When you need random access
- When size is known and fixed
- When memory efficiency matters
python.js
class Array:
def __init__(self, size):
self.data = [None] * size
self.length = 0
def get(self, index):
if 0 <= index < self.length:
return self.data[index]
raise IndexError("Index out of range")
def set(self, index, value):
if 0 <= index < len(self.data):
self.data[index] = value
if index >= self.length:
self.length = index + 1
else:
raise IndexError("Index out of range")
Linked Lists
Properties
- Access: O(n) - must traverse
- Search: O(n)
- Insertion: O(1) at head, O(n) at position
- Deletion: O(1) at head, O(n) at position
- Space: O(n)
Use Cases
- When size is unknown
- When frequent insertions/deletions at beginning
- When memory is fragmented
python.js
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class LinkedList:
def __init__(self):
self.head = None
def insert_at_head(self, val):
new_node = ListNode(val)
new_node.next = self.head
self.head = new_node
def delete_at_head(self):
if self.head:
self.head = self.head.next
Stacks
Properties
- Push: O(1)
- Pop: O(1)
- Peek: O(1)
- Space: O(n)
Use Cases
- Function call management
- Expression evaluation
- Undo/redo operations
- Backtracking algorithms
python.js
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
if self.is_empty():
raise IndexError("Stack is empty")
return self.items.pop()
def peek(self):
if self.is_empty():
raise IndexError("Stack is empty")
return self.items[-1]
def is_empty(self):
return len(self.items) == 0
Queues
Properties
- Enqueue: O(1)
- Dequeue: O(1)
- Front: O(1)
- Space: O(n)
Use Cases
- BFS traversal
- Task scheduling
- Request handling
- Print queue management
python.js
from collections import deque
class Queue:
def __init__(self):
self.items = deque()
def enqueue(self, item):
self.items.append(item)
def dequeue(self):
if self.is_empty():
raise IndexError("Queue is empty")
return self.items.popleft()
def front(self):
if self.is_empty():
raise IndexError("Queue is empty")
return self.items[0]
def is_empty(self):
return len(self.items) == 0
Trees
Binary Tree
python.js
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
Binary Search Tree (BST)
Properties:
- Left subtree < root < right subtree
- Search: O(log n) average, O(n) worst
- Insert: O(log n) average, O(n) worst
- Delete: O(log n) average, O(n) worst
Balanced Trees (AVL, Red-Black)
- Maintain height balance
- All operations: O(log n) guaranteed
Hash Tables
Properties
- Insert: O(1) average, O(n) worst
- Search: O(1) average, O(n) worst
- Delete: O(1) average, O(n) worst
- Space: O(n)
Use Cases
- Fast lookups
- Counting frequencies
- Caching
- Set operations
python.js
class HashTable:
def __init__(self, size=10):
self.size = size
self.table = [[] for _ in range(size)]
def _hash(self, key):
return hash(key) % self.size
def insert(self, key, value):
index = self._hash(key)
for pair in self.table[index]:
if pair[0] == key:
pair[1] = value
return
self.table[index].append([key, value])
def get(self, key):
index = self._hash(key)
for pair in self.table[index]:
if pair[0] == key:
return pair[1]
raise KeyError("Key not found")
Choosing the Right Data Structure
| Operation | Array | Linked List | Stack | Queue | BST | Hash Table |
|---|---|---|---|---|---|---|
| Access by index | O(1) | O(n) | - | - | - | - |
| Search | O(n) | O(n) | O(n) | O(n) | O(log n) | O(1) |
| Insert at beginning | O(n) | O(1) | O(1) | O(1) | O(log n) | O(1) |
| Delete | O(n) | O(n) | O(1) | O(1) | O(log n) | O(1) |
Practice Problems
- Implement a stack using two queues
- Implement a queue using two stacks
- Design a hash table with collision handling
- Implement BST with insert, delete, search
- Convert array to balanced BST