Phân tích độ phức tạp thuật toán (Big O Notation): Những cạm bẫy khi tính thời gian chạy
Big O không chỉ là đếm số vòng lặp for Nhiều lập trình viên có thói quen tính độ phức tạp thuật toán rất ngây thơ: Thấy 1 vòng lặp for thì kết luận là $ON$, thấy 2 vòng lặp for lồng nhau thì kết luận…
☰ Mục lục bài viết (7 phần) Mở / Đóng mục lục
Big O không chỉ là đếm số vòng lặp for#
Nhiều lập trình viên có thói quen tính độ phức tạp thuật toán rất ngây thơ: Thấy 1 vòng lặp for thì kết luận là $O(N)$, thấy 2 vòng lặp for lồng nhau thì kết luận là $O(N^2)$.
Thực tế, có rất nhiều cạm bẫy tiềm ẩn bên trong các phương thức có sẵn của ngôn ngữ khiến độ phức tạp thực tế nặng hơn bạn tưởng gấp nhiều lần.
3 Cạm bẫy Big O kinh điển cần tránh#
Cạm bẫy 1: Gọi hàm tốn $O(N)$ bên trong vòng lặp $O(N)$#
// ĐOẠN CODE NÀY LÀ O(N^2), KHÔNG PHẢI O(N)!
for (let i = 0; i < array.length; i++) {
if (array.includes(target[i])) { // Hàm includes() tự nó là một vòng lặp O(N)!
// do something
}
}
- Khắc phục: Chuyển mảng sang cấu trúc
Settrước vòng lặp để hàmset.has()chỉ tốn $O(1)$ $\rightarrow$ Tổng độ phức tạp giảm xuống $O(N)$!
Cạm bẫy 2: Nối chuỗi (String Concatenation) trong vòng lặp#
Trong các ngôn ngữ mà String là bất biến (Immutable như Java, JavaScript, Python):
String s = "";
for (int i = 0; i < n; i++) {
s += i; // Mỗi lần cộng chuỗi là tạo một object mới và copy toàn bộ chuỗi cũ!
}
Đoạn code trên có độ phức tạp là $O(N^2)$! Hãy luôn sử dụng StringBuilder trong Java hoặc mảng join() để đạt $O(N)$.
Cạm bẫy 3: Coi thường hằng số lớn (Large Constant Factor)#
Về mặt lý thuyết Big O, thuật toán $O(N)$ tốt hơn $O(N \log N)$. Nhưng nếu thuật toán $O(N)$ có hệ số ẩn là $1000N$, còn $O(N \log N)$ chỉ là $2N \log N$, thì với $N < 1.000.000$, thuật toán $O(N \log N)$ thực tế lại chạy nhanh hơn nhiều!
Bảng tra cứu độ phức tạp các cấu trúc dữ liệu thông dụng#
| Thao tác | Array | LinkedList | Hash Table | Binary Search Tree (AVL/Red-Black) |
|---|---|---|---|---|
| Tìm kiếm | $O(N)$ | $O(N)$ | $O(1)$ trung bình | $O(\log N)$ |
| Chèn ở đầu | $O(N)$ | $O(1)$ | N/A | $O(\log N)$ |
| Chèn ở cuối | $O(1)$ amortized | $O(1)$ (với tail) | $O(1)$ | $O(\log N)$ |
Góc thảo luận#
Bạn đã từng tối ưu một đoạn code từ $O(N^2)$ xuống $O(N)$ giúp giảm thời gian chạy từ vài chục phút xuống vài giây chưa?
Xem thêm nội dung liên quan:
Thảo luận
1 phản hồi
Tham gia thảo luận
Bài liên quan trong #Giải đề & Thuật toán
-
Áp dụng Self-Consistency và Chain-of-Thought trong Prompting cho bài toán logic phức tạp
Minh Trí (AI Engineer) · 💬 1 -
Concurrency trong Golang: Hiểu sâu về Goroutine leak và cách phòng tránh bằng context.Context
Hoàng Nam (Tech Lead) · 💬 1 -
Thiết kế hàng đợi ưu tiên (Priority Queue) cho hệ thống gọi xe công nghệ (Ride Hailing)
Hoàng Nam (Tech Lead) · 💬 1 -
Chuẩn bị phỏng vấn Live Coding: Chiến lược giải quyết bài toán khi bạn bị "đơ" trước mặt interviewer
Quốc Bảo (Fullstack Developer) · 💬 1 -
Giải bài toán hai con trỏ (Two Pointers) kinh điển trong LeetCode: Tìm cặp số có tổng bằng Target
Quốc Bảo (Fullstack Developer) · 💬 1