Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Hôm nay chúng ta sẽ cùng “đối mặt” với một trong những cấu trúc dữ liệu KHỦNG nhất: CÂY. Đừng lo nếu bạn cảm thấy choáng ngợp – hãy coi mỗi nút cây như một bài toán nhỏ, và Loser1 sẽ cùng bạn “chặt” từng nhánh một!
Cây là một cấu trúc dữ liệu phi tuyến tính, hierarchical (có cấp bậc) được sử dụng để biểu diễn và tổ chức dữ liệu theo cách phân cấp. Trong thực tế, chúng ta có thể thấy cấu trúc cây ở khắp mọi nơi: cây gia phả, cấu trúc thư mục trong máy tính, hay thậm chí là cấu trúc tổ chức của một công ty.
Một cây bao gồm các thành phần sau:
struct TreeNode {
int val; // Data stored in the node
TreeNode* left; // Pointer to left child
TreeNode* right; // Pointer to right child
// Constructor
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
Visualization:
A
/ \
B C
/ \ \
D E F
Cùng “đi lạc” trong khu rừng cây nhị phân với 4 cách duyệt cơ bản!
Code:
// Pre-order traversal: Root -> Left -> Right
void preOrder(Node* root) {
if (root == nullptr) return;
cout << root->data << " "; // Visit root
preOrder(root->left); // Traverse left
preOrder(root->right); // Traverse right
}
Giải thích:
Code:
// In-order traversal: Left -> Root -> Right
void inOrder(Node* root) {
if (root == nullptr) return;
inOrder(root->left); // Traverse left
cout << root->data << " "; // Visit root
inOrder(root->right); // Traverse right
}
Giải thích:
Code:
// Post-order traversal: Left -> Right -> Root
void postOrder(Node* root) {
if (root == nullptr) return;
postOrder(root->left); // Traverse left
postOrder(root->right); // Traverse right
cout << root->data << " "; // Visit root
}
Giải thích:
Code sử dụng Queue:
// Level-order traversal using BFS
void levelOrder(Node* root) {
if (root == nullptr) return;
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node* current = q.front();
q.pop();
cout << current->data << " ";
if (current->left) q.push(current->left);
if (current->right) q.push(current->right);
}
}
Giải thích:
Để minh họa các phương pháp duyệt cây này, hãy xem ví dụ sau:
Tiếp theo, tôi sẽ tiếp tục với phần Cây Nhị Phân Tìm Kiếm (BST) và các hoạt động cơ bản trên BST. Bạn có muốn tôi tiếp tục không?
Visualization:
8
/ \
3 10
/ \ \
1 6 14
BST – Nơi mọi nút trái đều nhỏ hơn cha, nút phải đều lớn hơn cha!
Code:
// BST Search: O(h) time complexity
Node* search(Node* root, int key) {
if (root == nullptr || root->data == key)
return root;
if (key < root->data)
return search(root->left, key);
else
return search(root->right, key);
}
Giải thích:
Code:
// BST Insertion: Maintain BST property
Node* insert(Node* root, int value) {
if (root == nullptr)
return new Node(value);
if (value < root->data)
root->left = insert(root->left, value);
else if (value > root->data)
root->right = insert(root->right, value);
return root;
}
Giải thích:
Code:
// BST Deletion: 3 cases
Node* deleteNode(Node* root, int key) {
if (root == nullptr) return root;
if (key < root->data)
root->left = deleteNode(root->left, key);
else if (key > root->data)
root->right = deleteNode(root->right, key);
else {
// Case 1: No child
if (root->left == nullptr && root->right == nullptr) {
delete root;
root = nullptr;
}
// Case 2: One child
else if (root->left == nullptr) {
Node* temp = root;
root = root->right;
delete temp;
}
else if (root->right == nullptr) {
Node* temp = root;
root = root->left;
delete temp;
}
// Case 3: Two children
else {
Node* successor = findMin(root->right);
root->data = successor->data;
root->right = deleteNode(root->right, successor->data);
}
}
return root;
}
Giải thích 3 Trường Hợp:
Code:
// Tree Height: Max depth from root to leaf
int height(Node* root) {
if (root == nullptr)
return -1; // or 0 depending on definition
return 1 + max(height(root->left), height(root->right));
}
Giải thích:
Code:
// Check BST using range
bool isBST(Node* root, Node* min = nullptr, Node* max = nullptr) {
if (root == nullptr) return true;
if ((min && root->data <= min->data) ||
(max && root->data >= max->data))
return false;
return isBST(root->left, min, root) &&
isBST(root->right, root, max);
}
Giải thích:
Trước khi thêm 7:
5
/ \
3 8
/ \
2 4
Sau khi thêm 7:
5
/ \
3 8
/ \ /
2 4 7
Node 7 “chui” vào vị trí hợp lệ bên trái 8
Xóa node 3 (có 2 con):
5 5
/ \ / \
3 8 ==> 4 8
/ \ /
2 4 2
Successor (4) thế chỗ 3, cây vẫn giữ nguyên tính chất BST
int countNodes(Node* root) {
if (root == nullptr) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
Gợi ý: Tổng = 1 (node hiện tại) + tổng nhánh trái + tổng nhánh phải
bool isSymmetric(Node* root) {
return isMirror(root, root);
}
bool isMirror(Node* t1, Node* t2) {
if (t1 == nullptr && t2 == nullptr) return true;
if (t1 == nullptr || t2 == nullptr) return false;
return (t1->data == t2->data) &&
isMirror(t1->right, t2->left) &&
isMirror(t1->left, t2->right);
}
Mẹo: Coi cây như 2 cây con và kiểm tra mirror từng cặp node
Cây nhị phân giống như cuộc đời mỗi người:
Hãy là một BST – Luôn biết vị trí của mình trong vũ trụ!
Hẹn gặp lại ở Phần 2: Balanced Trees – Khi cây học cách “giữ thăng bằng” trong cuộc sống!
Loser1 tip: Bạn thấy bài viết có ích? Hãy chia sẻ để 10 người bạn cùng đọc - vì chúng ta là những kẻ thất bại BIẾT CÁCH ĐỨNG LÊN!