Skip to Content
🧮 CTDL & Giải thuật⏱️ Độ phức tạp thuật toán

Độ phức tạp thuật toán (Big O Notation)

Big O là gì?

Big O Notation là cách đo lường hiệu suất của thuật toán dựa trên:

  • Thời gian (Time Complexity): Thuật toán chạy nhanh hay chậm?
  • Không gian (Space Complexity): Thuật toán dùng bao nhiêu bộ nhớ?
💡

Big O mô tả trường hợp xấu nhất (worst case) và cho biết thuật toán sẽ “scale” như thế nào khi dữ liệu tăng lên.

Các độ phức tạp phổ biến

Big OTên gọiVí dụ
O(1)ConstantTruy cập phần tử mảng
O(log n)LogarithmicBinary Search
O(n)LinearDuyệt mảng
O(n log n)LinearithmicMerge Sort, Quick Sort
O(n²)QuadraticNested loops
O(2ⁿ)ExponentialFibonacci đệ quy
O(n!)FactorialPermutations

O(1) - Constant Time

Thời gian thực thi không đổi dù dữ liệu lớn đến đâu.

def get_first(arr): return arr[0] # O(1) def add_numbers(a, b): return a + b # O(1) # Hash table lookup my_dict = {"name": "John", "age": 30} print(my_dict["name"]) # O(1)

O(log n) - Logarithmic Time

Mỗi bước chia đôi dữ liệu cần xử lý.

def binary_search(arr, target): left, right = 0, len(arr) - 1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return -1

O(n) - Linear Time

Thời gian tăng tỷ lệ thuận với kích thước dữ liệu.

def find_max(arr): max_val = arr[0] for num in arr: # Duyệt n phần tử if num > max_val: max_val = num return max_val

O(n²) - Quadratic Time

Thường do nested loops - vòng lặp lồng nhau.

def bubble_sort(arr): n = len(arr) for i in range(n): # O(n) for j in range(n - 1): # O(n) if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] return arr # Tổng: O(n × n) = O(n²)
⚠️

O(n²) trở lên thường không chấp nhận được với dữ liệu lớn. Hãy tìm cách tối ưu!

Space Complexity

Bộ nhớ sử dụng theo kích thước input.

# O(1) Space - In-place def reverse_inplace(arr): left, right = 0, len(arr) - 1 while left < right: arr[left], arr[right] = arr[right], arr[left] left += 1 right -= 1 # O(n) Space - Tạo mảng mới def reverse_copy(arr): return arr[::-1] # Tạo bản sao mới

Quy tắc tính Big O

1. Bỏ qua hằng số (Drop Constants)

Khi phân tích Big O, ta bỏ qua các hằng số nhân vì chúng không ảnh hưởng đến tốc độ tăng của thuật toán.

Giải thích: O(2n), O(3n), O(100n) đều tăng tuyến tính theo n, nên đều quy về O(n).

def example(arr): # Duyệt mảng 1 lần: n phép for x in arr: print(x) # Duyệt mảng lần 2: n phép for x in arr: print(x * 2) # Tổng: 2n phép → O(2n) → O(n)

2. Giữ lại số hạng lớn nhất (Drop Non-Dominant Terms)

Khi có nhiều số hạng, chỉ giữ lại số hạng tăng nhanh nhất vì nó sẽ “thống trị” khi n lớn.

Giải thích: Với n = 1000: n² = 1,000,000 nhưng n chỉ = 1000. Số hạng n “không đáng kể” so với n².

Biểu thứcĐơn giản
O(n² + n)O(n²)
O(n³ + n² + n)O(n³)
O(n + log n)O(n)
O(2ⁿ + n²)O(2ⁿ)
def example(arr): n = len(arr) # Phần 1: O(n²) for i in range(n): for j in range(n): print(i, j) # Phần 2: O(n) for i in range(n): print(i) # Tổng: O(n² + n) → O(n²)

3. Các phép toán liên tiếp = Cộng (Add for Sequential)

Khi có nhiều đoạn code chạy tuần tự (không lồng nhau), ta cộng độ phức tạp.

Giải thích: Nếu duyệt mảng A kích thước a, rồi duyệt mảng B kích thước b → O(a + b).

def process_two_arrays(arr1, arr2): # Duyệt arr1: O(a) với a = len(arr1) for x in arr1: print(x) # Duyệt arr2: O(b) với b = len(arr2) for y in arr2: print(y) # Tổng: O(a) + O(b) = O(a + b) # LƯU Ý: Không phải O(n) vì 2 mảng có kích thước khác nhau!

4. Vòng lặp lồng nhau = Nhân (Multiply for Nested)

Khi có vòng lặp lồng bên trong vòng lặp khác, ta nhân độ phức tạp.

Giải thích: Vòng ngoài chạy a lần, mỗi lần vòng trong chạy b lần → Tổng: a × b lần.

def nested_loops(arr1, arr2): # Với mỗi phần tử của arr1 (a lần) for x in arr1: # Duyệt toàn bộ arr2 (b lần) for y in arr2: print(x, y) # Tổng: O(a × b) # Nếu a = b = n thì O(n²)
⚠️

Phân biệt O(a + b) và O(a × b):

  • Tuần tự (sequential): for A rồi for BO(a + b)
  • Lồng nhau (nested): for A { for B }O(a × b)

Tóm tắt

Cần nhớ
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ)
Nested loops → O(n²)
Chia đôi mỗi bước → O(log n)
Tối ưu từ O(n²) → O(n) thường dùng Hash Table

Tiếp theo: Mảng (Array)

Last updated on