Độ phức tạp bộ nhớ (Space Complexity)
Bài viết này giúp bạn hiểu cách phân tích độ phức tạp bộ nhớ thông qua các ví dụ cụ thể với phân tích từng bước.
Space Complexity là gì?
Space Complexity đo lường lượng bộ nhớ bổ sung mà thuật toán cần sử dụng, không tính input.
Space Complexity chỉ tính bộ nhớ phụ trợ (auxiliary space) - bộ nhớ mà thuật toán tạo thêm, không bao gồm input!
Các mức độ phổ biến
| Space | Ý nghĩa | Ví dụ |
|---|---|---|
| O(1) | Constant - cố định | Biến đếm, swap |
| O(n) | Linear - tỷ lệ với n | Tạo mảng mới kích thước n |
| O(n²) | Quadratic | Ma trận 2D n×n |
| O(log n) | Logarithmic | Đệ quy chia đôi (call stack) |
Ví dụ 1: O(1) Space - In-place Operations
Bài toán
Đảo ngược mảng tại chỗ (in-place).
Python
def reverse_inplace(arr):
left = 0 # 1 biến int
right = len(arr) - 1 # 1 biến int
while left < right:
# Swap không cần biến tạm (Python)
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
return arrPhân tích bộ nhớ:
left: 4 bytes (int)right: 4 bytes (int)temp: 4 bytes (int)
Tổng: 12 bytes - không phụ thuộc n → O(1)
Dù mảng có 10 hay 10 triệu phần tử, ta vẫn chỉ dùng 3 biến!
Ví dụ 2: O(n) Space - Tạo mảng mới
Bài toán
Đảo ngược mảng bằng cách tạo mảng mới.
Python
def reverse_copy(arr):
n = len(arr)
result = [] # Mảng mới rỗng
for i in range(n - 1, -1, -1):
result.append(arr[i]) # Thêm n phần tử
return result
# result chứa n phần tử → O(n) spacePhân tích bộ nhớ:
n: 4 bytes (int)result: n × 4 bytes (n số int)i: 4 bytes (int)
Tổng: 4 + 4n + 4 = 4n + 8 bytes → O(n)
Ví dụ 3: So sánh O(1) vs O(n)
Bài toán
Tìm tất cả các cặp có tổng bằng target.
Cách 1: Brute Force - O(1) Space
Python
def find_pairs_brute(arr, target):
pairs = [] # Chỉ lưu kết quả
n = len(arr)
for i in range(n): # Không tạo cấu trúc phụ
for j in range(i + 1, n):
if arr[i] + arr[j] == target:
pairs.append((arr[i], arr[j]))
return pairs
# Space phụ trợ: O(1) (không tính output)Phân tích:
- Chỉ dùng biến
i,j,n - Không tạo cấu trúc dữ liệu phụ
Time: O(n²) | Space: O(1)
Cách 2: Hash Set - O(n) Space
Python
def find_pairs_hash(arr, target):
pairs = []
seen = set() # Set chứa tối đa n phần tử
for num in arr:
complement = target - num
if complement in seen:
pairs.append((complement, num))
seen.add(num) # Mỗi phần tử thêm 1 lần
return pairs
# Space phụ trợ: O(n) cho seenPhân tích:
seen: chứa tối đa n phần tử- Mỗi phần tử: ~4-8 bytes
Time: O(n) | Space: O(n)
Space-Time Tradeoff: Cách 2 nhanh hơn (O(n) vs O(n²)) nhưng tốn thêm O(n) bộ nhớ!
Ví dụ 4: O(log n) Space - Đệ quy
Bài toán
Binary Search với đệ quy.
Python
def binary_search_recursive(arr, target, left, right):
# Mỗi lần gọi đệ quy = 1 frame trên call stack
if left > right:
return -1
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
return binary_search_recursive(arr, target, mid + 1, right)
else:
return binary_search_recursive(arr, target, left, mid - 1)
# Gọi: binary_search_recursive(arr, target, 0, len(arr) - 1)Phân tích Call Stack:
| Lần gọi | Kích thước vùng tìm | Stack frames |
|---|---|---|
| 1 | n | 1 |
| 2 | n/2 | 2 |
| 3 | n/4 | 3 |
| k | n/2^(k-1) | k |
Dừng khi: n/2^(k-1) = 1 → k = log₂n
Space: O(log n) cho call stack
Đệ quy luôn tốn thêm bộ nhớ cho call stack! Binary Search iterative chỉ cần O(1) space.
Ví dụ 5: O(n) Space - Đệ quy tuyến tính
Bài toán
Tính giai thừa bằng đệ quy.
Python
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
# factorial(5)
# Call stack:
# factorial(5) đang chờ
# factorial(4) đang chờ
# factorial(3) đang chờ
# factorial(2) đang chờ
# factorial(1) = 1 ← trả vềPhân tích Call Stack:
- factorial(n) gọi factorial(n-1)
- factorial(n-1) gọi factorial(n-2)
- …
- factorial(1) trả về
Số frames tối đa: n frames → O(n) space
So sánh với Iterative
Python
def factorial_iterative(n):
result = 1 # 1 biến
for i in range(2, n + 1):
result *= i
return result
# Space: O(1) - chỉ cần 2 biếnVí dụ 6: O(n²) Space - Ma trận 2D
Bài toán
Tạo bảng nhân n×n.
Python
def multiplication_table(n):
# Tạo ma trận n x n
table = []
for i in range(1, n + 1):
row = [] # n rows
for j in range(1, n + 1):
row.append(i * j) # n columns mỗi row
table.append(row)
return table
# Tổng: n × n = n² phần tử → O(n²) spacePhân tích:
- Ma trận có n rows
- Mỗi row có n columns
- Tổng: n × n = n² phần tử
Space: O(n²)
Bảng tóm tắt
| Ví dụ | Auxiliary Space | Lý do |
|---|---|---|
| Reverse in-place | O(1) | Chỉ dùng vài biến |
| Reverse copy | O(n) | Tạo mảng mới n phần tử |
| Two Sum (brute) | O(1) | Không có cấu trúc phụ |
| Two Sum (hash) | O(n) | Set chứa n phần tử |
| Binary Search (recursive) | O(log n) | Call stack log n levels |
| Factorial (recursive) | O(n) | Call stack n levels |
| Factorial (iterative) | O(1) | Chỉ biến đếm |
| Ma trận n×n | O(n²) | n² phần tử |
Tips phân tích Space Complexity
Checklist phân tích Space:
- Biến đơn (int, float, bool): O(1)
- Mảng/List kích thước n: O(n)
- Ma trận n×n: O(n²)
- Hash Table/Set với n keys: O(n)
- Đệ quy: Đếm max call stack depth
Tiếp theo: Mảng (Array)