Ví dụ phân tích độ phức tạp thuật toán
Bài viết này sẽ giúp bạn hiểu cách phân tích độ phức tạp thông qua các ví dụ cụ thể với lời giải chi tiết từng bước.
Ví dụ 1: Tính tổng các số từ 1 đến n
Bài toán
Tính tổng 1 + 2 + 3 + ... + n
Cách 1: Vòng lặp
Python
def sum_1_to_n(n):
total = 0 # 1 phép gán
for i in range(1, n + 1): # Lặp n lần
total += i # 1 phép cộng + 1 phép gán
return total # 1 phép returnPhân tích:
- Khởi tạo
total: 1 phép - Vòng lặp chạy n lần, mỗi lần 2 phép (cộng và gán)
- Return: 1 phép
Tổng: 1 + 2n + 1 = 2n + 2 → O(n)
Cách 2: Công thức toán học
Python
def sum_1_to_n_formula(n):
return n * (n + 1) // 2 # 3 phép: nhân, cộng, chiaPhân tích:
- Chỉ có 3 phép cố định (dù n lớn đến đâu)
Tổng: 3 → O(1)
Cùng một bài toán nhưng thuật toán khác nhau có độ phức tạp khác nhau: O(n) vs O(1)!
Ví dụ 2: Tìm số lớn nhất trong mảng
Bài toán
Cho mảng n số nguyên, tìm số lớn nhất.
Python
def find_max(arr):
max_val = arr[0] # 1 phép
for i in range(1, len(arr)): # Lặp n-1 lần
if arr[i] > max_val: # 1 so sánh
max_val = arr[i] # 1 gán (có thể không chạy)
return max_val # 1 phépPhân tích:
- Khởi tạo: 1 phép
- Vòng lặp: (n-1) lần × 1-2 phép mỗi lần
- Return: 1 phép
Best case: Phần tử đầu là max → (n-1) so sánh, 0 gán Worst case: Mảng tăng dần → (n-1) so sánh, (n-1) gán
Tổng: ~ n phép → O(n)
Ví dụ 3: Kiểm tra số có trùng lặp
Bài toán
Cho mảng n số, kiểm tra xem có phần tử nào xuất hiện 2 lần không?
Cách 1: So sánh từng cặp (Brute Force)
Python
def has_duplicate_slow(arr):
n = len(arr)
for i in range(n): # Vòng 1: n lần
for j in range(i + 1, n): # Vòng 2: trung bình n/2 lần
if arr[i] == arr[j]: # 1 so sánh
return True
return FalsePhân tích:
- i=0: so sánh n-1 lần
- i=1: so sánh n-2 lần
- …
- i=n-2: so sánh 1 lần
Tổng: (n-1) + (n-2) + … + 1 = n(n-1)/2 ≈ n²/2 → O(n²)
Cách 2: Dùng Set (Hash Table)
Python
def has_duplicate_fast(arr):
seen = set() # O(1)
for num in arr: # n lần
if num in seen: # O(1) trung bình
return True
seen.add(num) # O(1) trung bình
return FalsePhân tích:
- Vòng lặp: n lần
- Mỗi lần: O(1) cho contains và add
Tổng: n × O(1) = O(n)
Space-time tradeoff: Cách 2 nhanh hơn O(n) nhưng cần thêm O(n) bộ nhớ cho Set!
Ví dụ 4: Binary Search
Bài toán
Tìm vị trí của target trong mảng đã sắp xếp.
Python
def binary_search(arr, target):
left, right = 0, len(arr) - 1 # O(1)
while left <= right: # Lặp bao nhiêu lần?
mid = (left + right) // 2 # O(1)
if arr[mid] == target: # O(1)
return mid
elif arr[mid] < target: # O(1)
left = mid + 1
else:
right = mid - 1
return -1Phân tích số lần lặp:
| Lần lặp | Kích thước vùng tìm kiếm |
|---|---|
| 1 | n |
| 2 | n/2 |
| 3 | n/4 |
| k | n/2^(k-1) |
Dừng khi: n/2^(k-1) = 1 → k = log₂n + 1
Tổng: O(log n)
Ví dụ 5: Nested Loops Analysis
Bài toán
Phân tích các dạng vòng lặp lồng nhau khác nhau.
Dạng 1: Hai vòng lặp độc lập
Python
def example_1(n):
for i in range(n): # n lần
print(i)
for j in range(n): # n lần
print(j)Phân tích: n + n = 2n → O(n)
Dạng 2: Hai vòng lặp lồng nhau
Python
def example_2(n):
for i in range(n): # n lần
for j in range(n): # n lần cho mỗi i
print(i, j)Phân tích: n × n = n² → O(n²)
Dạng 3: Vòng lặp giảm dần
Python
def example_3(n):
for i in range(n): # i từ 0 đến n-1
for j in range(i, n): # j từ i đến n-1
print(i, j)Phân tích:
- i=0: n lần
- i=1: n-1 lần
- …
- i=n-1: 1 lần
Tổng: n + (n-1) + … + 1 = n(n+1)/2 → O(n²)
Bảng tóm tắt
| Ví dụ | Thuật toán | Time | Space |
|---|---|---|---|
| Tổng 1-n | Vòng lặp | O(n) | O(1) |
| Tổng 1-n | Công thức | O(1) | O(1) |
| Tìm max | Duyệt | O(n) | O(1) |
| Kiểm tra trùng | Brute force | O(n²) | O(1) |
| Kiểm tra trùng | Set | O(n) | O(n) |
| Binary Search | Chia đôi | O(log n) | O(1) |
| 2 vòng lặp | Độc lập | O(n) | O(1) |
| 2 vòng lặp | Lồng nhau | O(n²) | O(1) |
Tiếp theo: Mảng (Array)