Độ phức tạp không gian
Tốc độ không phải thứ duy nhất quan trọng — bộ nhớ cũng vậy. Bài này dạy cách đo độ phức tạp
không gian (thuật toán dùng thêm bao nhiêu bộ nhớ theo n), và vì sao đôi khi ta cố tình tốn
thêm bộ nhớ để chạy nhanh hơn.
1. Trực quan: bàn làm việc rộng cỡ nào?
Thời gian giống bạn làm nhanh cỡ nào; không gian giống bàn làm việc của bạn cần rộng cỡ nào. Một thuật toán có thể nhanh nhưng ngốn bộ nhớ khổng lồ — và nếu hết RAM, máy sẽ chậm rề hoặc sập. Vì vậy ta đo cả hai.
Độ phức tạp không gian = lượng bộ nhớ thêm thuật toán cần, theo kích thước đầu vào n.
(Không tính phần đầu vào — ta tính phần phát sinh trong lúc chạy.)
2. Hai mức hay gặp
O(1) không gian (in-place — tại chỗ): chỉ dùng vài biến phụ, không phụ thuộc n.
def tong(A):
s = 0 # 1 bien duy nhat
for x in A:
s += x
return s # O(1) khong gian
O(n) không gian: tạo cấu trúc phụ lớn bằng dữ liệu (mảng mới, set, dict).
def binh_phuong(A):
kq = [] # mang moi, lon dan bang A
for x in A:
kq.append(x * x)
return kq # O(n) khong gian
3. Chạy debug: "nhìn" bộ nhớ thêm lớn lên
Bước qua hàm tạo mảng phụ với A = [2, 3, 4]. Để ý list kq phình ra bằng A:
So với hàm tong ở trên (chỉ 1 biến s, không đổi dù A to cỡ nào) — đó là khác biệt O(1) và O(n)
về không gian.
4. Đảo mảng: in-place (O(1)) vs tạo mới (O(n))
Cùng việc "đảo ngược mảng", hai cách tốn bộ nhớ khác nhau. Bấm Run:
Cả hai cùng O(n) thời gian, nhưng cách 2 chỉ tốn O(1) không gian (vài biến), còn cách 1 tốn O(n) (mảng mới). Khi dữ liệu khổng lồ, in-place tiết kiệm bộ nhớ đáng kể.
5. Đánh đổi thời gian ↔ không gian
Nhớ mẹo set ở bài 1.2.1? Ta tốn O(n) bộ nhớ (cái set) để hạ thời gian từ O(n²) xuống O(n). Đó là đánh đổi kinh điển:
Thường có thể đổi bộ nhớ lấy tốc độ (nhớ kết quả để khỏi tính lại) hoặc ngược lại. Không có bữa trưa miễn phí — chọn cái nào tùy bài toán và tài nguyên.
Quy hoạch động (học sau) chính là nghệ thuật đánh đổi này: tốn bộ nhớ lưu kết quả con để tránh tính đi tính lại.
6. Lưu ý: đệ quy cũng tốn không gian
Mỗi lời gọi hàm chiếm một chỗ trên ngăn xếp lời gọi (call stack). Đệ quy sâu n tầng → tốn
O(n) không gian dù không tạo mảng nào. Ta sẽ gặp lại ở bài 1.3.2.
7. Mẫu nhận diện
- Chỉ vài biến phụ, sửa tại chỗ → O(1) không gian.
- Tạo mảng/set/dict lớn bằng
n→ O(n) không gian. - Đệ quy sâu theo
n→ O(n) không gian (vì call stack).
8. 🎮 Trò chơi: Đoán độ phức tạp KHÔNG GIAN
9. Quiz kiểm tra nhanh
10. Hoàn thành
…☑️ Tiếp theo: 1.2.6 — Phân tích amortized (khi một thao tác thỉnh thoảng đắt nhưng trung bình vẫn rẻ).