Big O Notation & Complexity Analysis
What is Big O Notation?
Big O notation is a mathematical language used to describe the efficiency of an algorithm as the input size
1. Time Complexity
Focuses on the number of operations required relative to the input size.
- O(1): Constant Time (e.g., accessing an array index).
- O(log n): Logarithmic Time (e.g., Binary Search).
- O(n): Linear Time (e.g., iterating through a list).
- O(n log n): Log-linear Time (e.g., QuickSort, MergeSort).
- O(n²): Quadratic Time (e.g., nested loops, Bubble Sort).
- O(2âż): Exponential Time (e.g., recursive Fibonacci).
2. Space Complexity
Focuses on the amount of memory consumed by an algorithm (excluding the input itself, often called âAuxiliary Spaceâ).
The Golden Rule of Comparison
When analyzing algorithms, always consider the Worst Case scenario unless otherwise specified.
| Notation | Complexity | Name | Performance |
|---|---|---|---|
| O(1) | Excellent | Constant | The goal for data retrieval |
| O(log n) | Great | Logarithmic | Excellent for large data |
| O(n) | Fair | Linear | Standard for single passes |
| O(n log n) | Good | Linearithmic | Standard for efficient sorting |
| O(n²) | Poor | Quadratic | Avoid for large inputs |
| O(2âż) | Horrible | Exponential | Does not scale |
Practice Exercise
Compare the complexity of two approaches to solve the âTwo Sumâ problem: finding indices of two numbers that add up to a target.
Answer
1. Brute-Force Approach ( Time)
// Time Complexity: O(n^2) - Nested loops
// Space Complexity: O(1) - No extra data structures
public int[] TwoSumBruteForce(int[] nums, int target) {
for (int i = 0; i < nums.Length; i++) {
for (int j = i + 1; j < nums.Length; j++) {
if (nums[i] + nums[j] == target) {
return new int[] { i, j };
}
}
}
return null;
}
2. Optimized Hash Map Approach ( Time)
// Time Complexity: O(n) - Single pass through the array
// Space Complexity: O(n) - Dictionary scales with input size
public int[] TwoSumOptimized(int[] nums, int target) {
var map = new Dictionary<int, int>();
for (int i = 0; i < nums.Length; i++) {
int complement = target - nums[i];
if (map.ContainsKey(complement)) {
return new int[] { map[complement], i };
}
map[nums[i]] = i;
}
return null;
}
Reasoning
- Time Trade-off: The brute-force solution checks every possible pair, leading to
operations. The optimized solution trades Memory (Space) for Speed (Time). By using a hash map to remember values weâve already seen, we can find the âcomplementâ in near-constant time ( ) rather than scanning the remaining array again. - Scalability: If
, the solution would take roughly 1 Trillion operations, while the solution would take only 1 Million. This is the difference between a task finishing in milliseconds vs. hours.
Summary
Complexity analysis is the lens through which we view software performance. By mastering Big O, you can articulate why one piece of code is superior to another, even if they both produce the same result.