Space Complexity
Space Complexity
You've learned that time complexity measures how fast an algorithm runs. But there's another important question: How much memory does it use?
Imagine you're packing for a trip. You could bring everything you own (uses lots of space), or pack only essentials (uses minimal space). Similarly, algorithms can use memory efficiently or wastefully.
Space complexity measures the amount of memory an algorithm needs as the input size grows.
Why Space Complexity Matters
Consider these scenarios:
Scenario 1: Mobile App
- ›Limited RAM (2-4 GB)
- ›Algorithm using O(n²) space might crash the app
- ›Algorithm using O(1) space runs smoothly
Scenario 2: Big Data Processing
- ›Processing millions of records
- ›O(n) extra space = Gigabytes of RAM needed
- ›O(log n) extra space = Minimal RAM needed
Scenario 3: Embedded Systems
- ›Microcontrollers with only 2 KB RAM
- ›Every byte counts
- ›Must use O(1) space algorithms
Key Idea: Space complexity measures how much extra memory an algorithm needs relative to input size.
Total Space vs Auxiliary Space
Total Space Complexity
Total space = Space for input + Space for auxiliary variables
Auxiliary Space Complexity
Auxiliary space = Extra space used by algorithm (excluding input)
Example:
1def sum_array(arr):
2 total = 0 # Extra variable: 1 unit
3 for num in arr: # No extra space
4 total += num
5 return total
6
7# Input space: n (the array)
8# Auxiliary space: O(1) (only 'total' variable)
9# Total space: O(n) (input array + total variable)Note: We usually focus on auxiliary space because input size is given and constant.
Understanding O(1) Space - Constant Space
Definition: Algorithm uses a fixed amount of extra memory, regardless of input size.
Example 1: Simple Variables
1def find_max(arr):
2 max_val = arr[0] # 1 variable
3
4 for num in arr:
5 if num > max_val:
6 max_val = num # Update same variable
7
8 return max_val
9
10# Extra space: 1 variable (max_val)
11# Space Complexity: O(1)Analysis:
- ›Input size: 10, 100, or 1,000,000 elements
- ›Extra variables: Always just 1 (max_val)
- ›Space Complexity: O(1)
Example 2: Swap Two Numbers
1def swap(a, b):
2 temp = a # 1 extra variable
3 a = b
4 b = temp
5 return a, b
6
7# Extra space: 1 variable (temp)
8# Space Complexity: O(1)Understanding O(n) Space - Linear Space
Definition: Extra memory grows proportionally with input size.
Example 1: Copy Array
1def copy_array(arr):
2 new_arr = [] # New array
3
4 for num in arr:
5 new_arr.append(num) # Add to new array
6
7 return new_arr
8
9# Input size: n
10# Extra space: n (new array of same size)
11# Space Complexity: O(n)Example 2: Reverse Array
1def reverse_array(arr):
2 reversed_arr = [] # New array
3
4 for i in range(len(arr) - 1, -1, -1):
5 reversed_arr.append(arr[i])
6
7 return reversed_arr
8
9# Input: [1, 2, 3, 4, 5]
10# Output: [5, 4, 3, 2, 1]
11# Extra space: n elements
12# Space Complexity: O(n)Better Approach: In-Place Reversal (O(1) Space)
1def reverse_in_place(arr):
2 left = 0
3 right = len(arr) - 1
4
5 while left < right:
6 # Swap elements
7 arr[left], arr[right] = arr[right], arr[left]
8 left += 1
9 right -= 1
10
11 return arr
12
13# Extra space: Only 2 variables (left, right)
14# Space Complexity: O(1)Comparison:
- ›First approach: O(n) space, creates new array
- ›Second approach: O(1) space, modifies original array
- ›Trade-off: Less space but modifies input
Understanding O(n²) Space - Quadratic Space
Definition: Extra memory grows with the square of input size.
Example: 2D Matrix
1def create_matrix(n):
2 matrix = []
3
4 for i in range(n):
5 row = []
6 for j in range(n):
7 row.append(i * j)
8 matrix.append(row)
9
10 return matrix
11
12# For n = 3:
13# [[0, 0, 0],
14# [0, 1, 2],
15# [0, 2, 4]]
16#
17# Space: n × n = n² elements
18# Space Complexity: O(n²)Recursion and Space Complexity
Recursive functions use extra space on the call stack.
Example 1: Factorial (O(n) Space)
1def factorial(n):
2 if n <= 1:
3 return 1
4 return n * factorial(n - 1)
5
6# factorial(5) calls:
7# factorial(5) → factorial(4) → factorial(3) → factorial(2) → factorial(1)
8#
9# Call stack depth: n
10# Space Complexity: O(n) due to recursion stackExample 2: Iterative Factorial (O(1) Space)
1def factorial_iterative(n):
2 result = 1
3 for i in range(2, n + 1):
4 result *= i
5 return result
6
7# No recursion, no call stack
8# Only one variable: result
9# Space Complexity: O(1)Comparison:
- ›Recursive: O(n) space (call stack)
- ›Iterative: O(1) space (just variables)
Common Space Complexities Compared
| Space Complexity | Example | Memory for n=100 | Memory for n=1000 |
|---|---|---|---|
| O(1) | Finding max | Constant | Constant |
| O(log n) | Binary search (recursive) | ~7 calls | ~10 calls |
| O(n) | Copying array | 100 units | 1,000 units |
| O(n log n) | Merge sort | 100 × 7 | 1,000 × 10 |
| O(n²) | 2D matrix | 10,000 units | 1,000,000 units |
Space-Time Trade-offs
Sometimes we can trade space for time, or time for space.
Example: Fibonacci Sequence
Approach 1: Recursive (Slow, Less Space)
1def fibonacci_recursive(n):
2 if n <= 1:
3 return n
4 return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)
5
6# Time: O(2^n) - Very slow!
7# Space: O(n) - Call stack depthApproach 2: Memoization (Fast, More Space)
1def fibonacci_memo(n, memo={}):
2 if n in memo:
3 return memo[n]
4 if n <= 1:
5 return n
6
7 memo[n] = fibonacci_memo(n - 1, memo) + fibonacci_memo(n - 2, memo)
8 return memo[n]
9
10# Time: O(n) - Much faster!
11# Space: O(n) - Store results in dictionaryApproach 3: Iterative (Fast, Minimal Space)
1def fibonacci_iterative(n):
2 if n <= 1:
3 return n
4
5 prev2 = 0
6 prev1 = 1
7
8 for i in range(2, n + 1):
9 current = prev1 + prev2
10 prev2 = prev1
11 prev1 = current
12
13 return prev1
14
15# Time: O(n) - Fast
16# Space: O(1) - Minimal space!Comparison:
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Recursive | O(2^n) | O(n) | Slow, minimal space |
| Memoization | O(n) | O(n) | Fast, uses extra space |
| Iterative | O(n) | O(1) | Fast, minimal space - Best! |
How to Calculate Space Complexity
Rule 1: Count Variables
1def example(n):
2 a = 0 # 1 variable
3 b = 0 # 1 variable
4 c = 0 # 1 variable
5
6 for i in range(n):
7 print(a + b + c)
8
9# Space: 3 variables + loop counter
10# Space Complexity: O(1) - constantRule 2: Count Data Structures
1def example(n):
2 arr = [0] * n # Array of size n
3 result = 0 # 1 variable
4
5 for num in arr:
6 result += num
7
8# Space: n (array) + 1 (result)
9# Space Complexity: O(n)Rule 3: Count Recursion Depth
1def count_down(n):
2 if n == 0:
3 return
4 print(n)
5 count_down(n - 1)
6
7# count_down(5) → count_down(4) → count_down(3) → ...
8# Call stack: n levels deep
9# Space Complexity: O(n)Practice Problems
Problem 1: What's the Space Complexity?
1def mystery(arr):
2 total = 0
3 for i in range(len(arr)):
4 for j in range(len(arr)):
5 total += arr[i] * arr[j]
6 return total
7
8# Answer: O(1)
9# Explanation: Only 'total' variable, loops don't create extra spaceProblem 2: Reduce Space Complexity
Given this function, can you reduce its space complexity?
1# Original: O(n) space
2def sum_first_k(arr, k):
3 subset = arr[:k] # Creates new array
4 total = sum(subset)
5 return total
6
7# Improved: O(1) space
8def sum_first_k_optimized(arr, k):
9 total = 0
10 for i in range(k):
11 total += arr[i]
12 return total
13
14# No extra array needed!Key Takeaways
Space Complexity measures extra memory used by an algorithm.
Auxiliary Space excludes input size (focus on extra variables/structures).
Common Complexities: O(1) < O(log n) < O(n) < O(n²)
Recursion uses O(depth) space for the call stack.
Trade-offs: Sometimes we use more space to get faster time.
In-place algorithms modify input directly to save space (O(1) instead of O(n)).
What's Next?
Now that you understand space complexity:
- ›Asymptotic Analysis - Deep dive into Big-O, Omega, Theta
- ›Array Operations - Apply complexity analysis to arrays
- ›Recursion - Master recursive space complexity
Practice Exercises
Determine the space complexity:
Exercise 1
1def exercise1(n):
2 result = []
3 for i in range(n):
4 result.append(i * 2)
5 return result
6
7# Answer: O(n) - creates array of size nExercise 2
1def exercise2(n):
2 if n <= 0:
3 return 1
4 return n * exercise2(n - 1)
5
6# Answer: O(n) - recursion depth of nReady to continue? Let's explore Asymptotic Analysis next!