Bài 30: Binary Search Tree (BST) - Cây Tìm Kiếm Nhị Phân¶
Tác giả: FPTOJ Team
Nội dung tham khảo từ: CP-Algorithms, GeeksforGeeks - BST
Bản chất vấn đề¶
Cho một tập hợp số, cần hỗ trợ các thao tác: thêm, tìm kiếm, xóa, tìm min/max, tìm cận dưới (lower_bound).
Cách 1: Mảng — thêm $O(1)$, tìm $O(N)$. Chậm khi tập lớn.
Cách 2: set / map (Red-Black Tree) — tất cả $O(\log N)$. Tốt, nhưng là "hộp đen", không hiểu cấu trúc bên trong.
Cách 3: BST — tất cả $O(\log N)$ trung bình, và bạn hiểu rõ tại sao.
Binary Search Tree là cây nhị phân mà tại mỗi nút, mọi giá trị ở cây con trái đều nhỏ hơn nút đó, mọi giá trị ở cây con phải đều lớn hơn. Tính chất này cho phép tìm kiếm theo kiểu "chia đôi" y hệt tìm kiếm nhị phân trên mảng.
Với cây trên, tìm số 7 chỉ mất 4 bước: tại 8 (7 nhỏ hơn, đi trái) → tại 3 (7 lớn hơn, đi phải) → tại 6 (7 lớn hơn, đi phải) → tại 7 (tìm thấy). So với duyệt mảng mất 8 bước, BST khai thác triệt để tính chất thứ tự để loại bỏ một nửa ứng viên mỗi bước.
Tư duy cốt lõi¶
Thao tác 1: Tìm kiếm (Search)¶
Tại mỗi nút, so sánh giá trị cần tìm $v$ với $node \to val$:
$v < node \to val$→ đi trái$v > node \to val$→ đi phải$v = node \to val$→ tìm thấy
Nếu đi đến NULL thì giá trị không tồn tại trong cây.
Thao tác 2: Chèn (Insert)¶
Đi từ gốc, so sánh $v$ với từng nút để tìm vị trí lá thích hợp. Khi đến NULL, tạo nút mới tại đó.
Quy trình: tại 8 (5 nhỏ hơn, trái) → tại 3 (5 lớn hơn, phải) → tại 6 (5 nhỏ hơn, trái) → tại 4 (5 lớn hơn, phải) → tạo nút 5.
Thao tác 3: Xóa (Delete)¶
Xóa phức tạp hơn vì phải đảm bảo tính chất BST sau khi xóa. Có 3 trường hợp:
Trường hợp 1: Nút lá (0 con) — xóa trực tiếp, trả về NULL.
Trường hợp 2: Có 1 con — thay nút bằng con duy nhất.
Trường hợp 3: Có 2 con — tìm nút kế nhiệm (min bên phải) hoặc tiền nhiệm (max bên trái), sao chép giá trị vào nút cần xóa, rồi xóa nút kế nhiệm/tiền nhiệm ở vị trí cũ.
Minh họa xóa nút gốc 8 (có 2 con): tìm min bên phải là 10, thay 8 bằng 10, rồi xóa 10 ở cây con phải.
Thao tác 4: Tìm Min / Max¶
- Min: đi liên tục sang trái từ gốc
- Max: đi liên tục sang phải từ gốc
Với cây ở trên: min là 1 (8→3→1), max là 14 (8→10→14).
Duyệt Inorder¶
Duyệt theo thứ tự Trái → Gốc → Phải trên BST luôn cho ra dãy tăng dần. Đây là tính chất quan trọng nhất của BST, xuất phát trực tiếp từ tính chất "trái < gốc < phải".
Phân tích tính đúng đắn¶
Tại sao BST cho phép tìm kiếm nhị phân?¶
Mọi nút $u$ trong BST đều thoả mãn: tất cả giá trị trong cây con trái của $u$ nhỏ hơn $u \to val$, tất cả giá trị trong cây con phải lớn hơn $u \to val$$. Khi so sánh\(v\)với\(u \to val\)`:
- Nếu
$v < u \to val$, ta loại bỏ toàn bộ cây con phải (tất cả đều lớn hơn$u \to val$, do đó lớn hơn$v$). - Nếu
$v > u \to val$, ta loại bỏ toàn bộ cây con trái.
Mỗi bước loại bỏ ít nhất một nửa số nút còn lại, nên số bước tối đa là chiều cao $h$ của cây.
Tại sao xóa đúng khi dùng kế nhiệm?¶
Nút kế nhiệm (min bên phải) là nút nhỏ nhất trong cây con phải, thoả mãn:
- Lớn hơn mọi nút ở cây con trái (đảm bảo tính chất BST khi thay thế)
- Là nút lá hoặc chỉ có con phải (dễ xóa, thuộc trường hợp 1 hoặc 2)
Sao chép giá trị kế nhiệm vào nút cần xóa không vi phạm tính chất BST. Sau đó xoá nút kế nhiệm ở vị trí cũ — thao tác này quy về trường hợp 0 hoặc 1 con, luôn đúng đệ quy.
Tại sao inorder cho dãy tăng dần?¶
Chứng minh bằng quy nạp: giả sử cây con trái cho dãy tăng dần $L$, cây con phải cho dãy tăng dần $R$. Theo tính chất BST, mọi phần tử trong $L$ nhỏ hơn gốc, mọi phần tử trong $R$ lớn hơn gốc. Duyệt Trái → Gốc → Phải tạo dãy $L$, gốc, $R$ — đúng thứ tự tăng dần.
Đánh giá độ phức tạp¶
Độ phức tạp thời gian¶
Gọi $h$ là chiều cao cây, $N$ là số nút.
| Thao tác | Trung bình | Worst case |
|---|---|---|
| Tìm kiếm | $O(h) = O(\log N)$ |
$O(N)$ |
| Chèn | $O(h) = O(\log N)$ |
$O(N)$ |
| Xóa | $O(h) = O(\log N)$ |
$O(N)$ |
| Min/Max | $O(h) = O(\log N)$ |
$O(N)$ |
| Duyệt inorder | $O(N)$ |
$O(N)$ |
Worst case xảy ra khi BST bị méo (chèn dãy tăng/giảm dần), cây trở thành linked list, $h = N$.
Chèn 1, 2, 3, 4, 5 vào BST rỗng tạo cây nghiêng hoàn toàn — chiều cao $N = 5$, mọi thao tác đều $O(N)$.
Độ phức tạp bộ nhớ¶
- Cấu trúc:
$O(N)$cho$N$nút - Đệ quy:
$O(h)$stack frame (worst case$O(N)$)
BST tự cân bằng¶
Các biến thể giữ $h = O(\log N)$ luôn đúng:
| Cây | Chiều cao đảm bảo | Cơ chế |
|---|---|---|
| AVL Tree | $h \leq 1.44 \log_2 N$ |
Chênh lệch chiều cao 2 con $\leq 1$ |
| Red-Black Tree | $h \leq 2 \log_2(N+1)$ |
Tô màu đỏ-đen, đảm bảo cân bằng |
| B-Tree | $O(\log N)$ |
Mỗi nút nhiều khoá, dùng trong database |
Code hoàn chỉnh¶
Ứng dụng¶
Duyệt khoảng (Range Query)¶
Tìm tất cả phần tử trong khoảng $[L, R]$ trên BST, khai thác tính chất thứ tự để bỏ qua nhánh không cần thiết.
Độ phức tạp: $O(k + h)$ với $k$ là số phần tử trong khoảng, $h$ là chiều cao cây.
Kiểm tra cây có phải BST¶
Dùng kỹ thuật truyền khoảng hợp lệ $[minVal, maxVal]$ khi duyệt đệ quy.
BST so với Heap¶
| Tiêu chí | BST | Heap |
|---|---|---|
| Tính chất | trái < gốc < phải | gốc $\geq$ con (max-heap) |
| Tìm kiếm | $O(\log N)$ |
$O(N)$ |
| Duyệt inorder | Tăng dần | Không có ý nghĩa |
| Ứng dụng | set, map |
Priority Queue |
Lưu ý và cạm bẫy¶
-
BST bị méo: Chèn dãy tăng/giảm dần → cây nghiêng, thoái hoá thành linked list
$O(N)$. Trong thi đấu, dùngset/map(C++) đã cài Red-Black Tree sẵn thay vì tự cài BST thường. -
Không lưu trùng: BST cơ bản bỏ qua phần tử trùng. Nếu cần đếm số lần xuất hiện, thêm trường
cntvào nút. -
Đệ quy sâu: BST không cân bằng có thể sâu
$O(N)$, gây tràn stack đệ quy. Chuyển sang cài đặt iterative nếu cần.
Bài tập luyện tập¶
| Bài | Nền tảng | Độ khó | Chủ đề |
|---|---|---|---|
| LeetCode - Validate BST | LC | ⭐⭐ | Kiểm tra BST |
| LeetCode - Lowest Common Ancestor of BST | LC | ⭐⭐ | LCA trên BST |
| LeetCode - Kth Smallest Element in BST | LC | ⭐⭐ | Duyệt inorder |
| LeetCode - Insert into BST | LC | ⭐ | Chèn BST |
| LeetCode - Delete Node in BST | LC | ⭐⭐⭐ | Xóa BST |
| CSES - Tree Traversals | CSES | ⭐⭐ | BST traversal |
| CSES - Distinct Colors | CSES | ⭐⭐⭐ | Subtree query |
Tài liệu tham khảo¶
- CP-Algorithms - BST
- GeeksforGeeks - BST
- VNOI Wiki - Cấu trúc dữ liệu tổng quan
- YouTube - BST (takeuforward)
Bài trước: Sparse Table ← | Bài tiếp theo: Bao lồi →