You are on page 1of 52

Bài 2

Câu 1. Thực hiện các yêu cầu sau đây: Trình bày khái niệm cấu trúc vector. - Lập trình phương thức xóa
(erase) một phần tử của vector. - Lập trình phương thức thêm một
phần tử vào vị trí bất kỳ của vector. Lưu ý: vector được cài đặt bằng
mảng.
Câu 2. Thực hiện các yêu cầu sau đây:
- Trình bày cấu trúc danh sách liên kết đơn (single list) (vẽ hình minh
họa).
- Lập trình phương thức xóa (erase) một phần tử của danh sách liên kết
đơn.
- Lập trình phương thức thêm một phần tử vào đầu (push_front) danh
sách liên kết đơn.
Câu 3. Thực hiện các yêu cầu sau đây:
- Trình bày cấu trúc danh sách liên kết kép (double list) (vẽ hình minh
họa).
- Lập trình phương thức xóa một phần tử của danh sách liên kết kép.
- Lập trình phương thức thêm một phần tử vào đầu danh sách liên kết
kép
Câu 4. Thực hiện các yêu cầu sau đây:
Khái niệm cây, trình bày cấu trúc lớp cây tổng quát .
- Lập trình phương thức thêm một nút mới vào cây.
Câu 5. Thực hiện các yêu cầu sau đây:
Khái niệm cây nhị phân. - Lập trình phương thức thêm (insert) một nút
mới vào cây nhị phân. - Lập trình phương thức xóa (remove) một nút
trên cây nhị phân.
Câu 6. Thực hiện các yêu cầu sau đây:
- Định nghĩa ngăn xếp (Stack), trình bày cấu trúc dữ liệu stack, vẽ hình
minh họa, lấy 2 ví dụ trong thực tế có cấu trúc stack. - Trình bày thuật
toán chuyển đổi biểu thức dạng trung tổ sang dạng hậu tổ.
Câu 7. Thực hiện các yêu câu sau đây:
- Định nghĩa hàng đợi (Queue), trình bày về cấu trúc dữ liệu queue, vẽ
hình minh họa, lấy 2 ví dụ ứng dụng cấu trúc hàng đợi.
- Lập trình thuật toán
Câu 8. Thực hiện các yêu câu sau đây:
- Định nghĩa cây tìm kiếm nhị phân, trình bày cấu trúc lớp cây tìm kiếm
nhị phân
- Lập trình phương thức tìm kiếm trên cây.
Câu 9. Thực hiện các yêu câu sau đây:
- Định nghĩa cây tìm kiếm nhị phân, trình bày cấu trúc lớp cây tìm kiếm
nhị phân
- Lập trình phương thức thêm một nút mới vào cây tìm kiếm nhị phân.
- Lập trình phương thức tìm kiếm trên tìm kiếm nhị phân.
Câu 10. Thực hiện các yêu cầu sau đây:
- Định nghĩa cây tìm kiếm nhị phân, trình bày cấu trúc lớp cây tìm kiếm
nhị phân
- Lập trình phương thức thêm một nút mới vào cây tìm kiếm nhị phân.
- Lập trình phương thức xóa một nút trên cây tìm kiếm nhị phân.
Câu 11. Thực hiện các yêu cầu sau đây:
- Định nghĩa cây tìm kiếm nhị phân, trình bày cấu trúc lớp cây tìm kiếm
nhị phân - Vẽ cây tìm kiếm nhị phân với các nút lần lượt có giá trị sau:
246, 123, 132, 542, 65, 354, 165, 567, 80, 188, 220, 75.
Câu 12. Thực hiện các yêu câu sau đây:
- Trình bày cấu trúc bảng băm (Hashtable)
- Cài đặt phương thức tìm kiểm trên bảng băm
Câu 13. Thực hiện các yêu cầu sau đây:
Trình bày cấu trúc bảng băm (Hashtable)
- Vẽ cấu trúc của bảng băm với các giá trị khóa lần lượt: 564, 123, 679,
542, 65,
354, 165, 567, 80, 434. Biết rằng kích thước của bảng m = 9 và hàm
băm là hàm
chia.
Câu 1

1. Khái Niệm Cấu Trúc Vector:

Trong ngôn ngữ lập trình, cấu trúc vector thường được hiểu như một
dạng của mảng có kích thước linh hoạt, tự động mở rộng khi cần thiết.
Vector thường đi kèm với các phương thức và thuộc tính giúp quản lý
dễ dàng hơn so với mảng truyền thống.

Trong nhiều ngôn ngữ lập trình, vector thường được triển khai dưới
dạng một cấu trúc dữ liệu động, tự động quản lý kích thước và cung cấp
nhiều phương thức hữu ích như thêm, xóa, truy xuất phần tử, v.v.

2. Lập Trình Phương Thức Xóa (Erase) Một Phần Tử Của Vector:
#include <iostream>
#include <vector>
class MyVector {
private:
int* array; // Mảng chứa dữ liệu của vector
size_t size; // Kích thước hiện tại của vector
public:
// Hàm xóa một phần tử tại vị trí index
void erase(size_t index) {
if (index >= size) {
std::cout << "Index out of bounds\n";
return;
}
for (size_t i = index; i < size - 1; ++i) {
array[i] = array[i + 1];
}
--size;
}
return 0;
}
3. Lập Trình Phương Thức Thêm Một Phần Tử Vào Vị Trí Bất Kỳ Của
Vector:
#include <iostream>
#include <vector>
class MyVector {
private:
int* array; // Mảng chứa dữ liệu của vector
size_t size; // Kích thước hiện tại của vector
public:
// Hàm thêm một phần tử vào vị trí index
void insert(size_t index, int value) {
if (index > size) {
std::cout << "Index out of bounds\n";
return;
}
// Tạo mảng mới có kích thước lớn hơn 1
int* newArray = new int[size + 1];
// Sao chép các phần tử từ mảng cũ sang mảng mới
for (size_t i = 0; i < index; ++i) {
newArray[i] = array[i];
}
// Thêm phần tử mới vào vị trí index
newArray[index] = value;
// Sao chép các phần tử còn lại
for (size_t i = index; i < size; ++i) {
newArray[i + 1] = array[i];
}
// Giải phóng bộ nhớ của mảng cũ
delete[] array;
// Cập nhật con trỏ array để trỏ đến mảng mới
array = newArray;
// Tăng kích thước của vector
++size;
}
return 0;
}
Câu 2
1. Cấu Trúc Danh Sách Liên Kết Đơn (Single Linked List):
Danh sách liên kết đơn là một cấu trúc dữ liệu mà mỗi phần tử được
gọi là "node" và bao gồm dữ liệu và một con trỏ chỉ đến phần tử tiếp
theo trong danh sách. Mỗi node chứa dữ liệu và một con trỏ đến
node tiếp theo trong danh sách. Node cuối cùng có con trỏ trỏ đến
NULL, đánh dấu sự kết thúc của danh sách.
2. Lập Trình Phương Thức Xóa (Erase) Một Phần Tử Của Danh Sách
Liên Kết Đơn:
#include <iostream>

template <typename T>


class Node {
public:
T data;
Node* next;
Node(T value) : data(value), next(nullptr) {}
};
template <typename T>
class LinkedList {
private:
Node<T>* head;
public:
LinkedList() : head(nullptr) {}
// Hàm xóa một phần tử có giá trị value trong danh sách
void erase(T value) {
Node<T>* current = head;
Node<T>* previous = nullptr;
while (current != nullptr && current->data != value) {
previous = current;
current = current->next;
}
if (current == nullptr) {
std::cout << "Element not found in the list\n";
return;
}
if (previous == nullptr) {
// Nếu phần tử cần xóa là phần tử đầu tiên
head = current->next;
} else {
// Ngược lại, nối các node trước và sau node cần xóa
previous->next = current->next;
}
delete current;
}
// Các hàm khác như khởi tạo, thêm phần tử, in danh sách, v.v.,
cũng cần được triển khai
};
int main() {
LinkedList<int> myList;
// Thêm các phần tử vào danh sách
// ...
// In danh sách trước khi xóa
// ...
// Xóa phần tử có giá trị 42
myList.erase(42);
// In danh sách sau khi xóa
// ...
return 0;
}
3. Lập Trình Phương Thức Thêm Một Phần Tử Vào Đầu (Push
Front) Danh Sách Liên Kết Đơn:
#include <iostream>
template <typename T>
class Node {
public:
T data;
Node* next;
Node(T value) : data(value), next(nullptr) {}
};
template <typename T>
class LinkedList {
private:
Node<T>* head;
public:
LinkedList() : head(nullptr) {}
// Hàm thêm một phần tử vào đầu danh sách
void push_front(T value) {
Node<T>* newNode = new Node<T>(value);
// Nối node mới với node đầu tiên hiện tại
newNode->next = head;
// Cập nhật con trỏ đầu tiên để trỏ đến node mới
head = newNode;
}
// Các hàm khác như khởi tạo, xóa phần tử, in danh sách, v.v.,
cũng cần được triển khai
};
int main() {
LinkedList<int> myList;
// Thêm các phần tử vào danh sách
// ...
// In danh sách trước khi thêm vào đầu
// ...
// Thêm phần tử mới vào đầu danh sách
myList.push_front(99);
// In danh sách sau khi thêm vào đầu
// ...
return 0;
}
Câu 3
1. Cấu Trúc Danh Sách Liên Kết Kép (Double Linked List):

Danh sách liên kết kép bao gồm các node mà mỗi node không chỉ chứa
con trỏ đến node tiếp theo mà còn chứa con trỏ đến node trước đó.
Cấu trúc của một node trong danh sách liên kết kép có thể được mô tả
như sau:

+---+ +---+ +---+


| | <--> | | <--> | | <--> ... <--> NULL
+---+ +---+ +---+
^ ^ ^
| | |
prev data next
2. Lập Trình Phương Thức Xóa Một Phần Tử Của Danh Sách Liên
Kết Kép:
#include <iostream>
template <typename T>
class Node {
public:
T data;
Node* prev;
Node* next;
Node(T value) : data(value), prev(nullptr), next(nullptr) {}
};
template <typename T>
class DoublyLinkedList {
private:
Node<T>* head;
public:
DoublyLinkedList() : head(nullptr) {}
// Hàm xóa một phần tử có giá trị value trong danh sách
void erase(T value) {
Node<T>* current = head;
while (current != nullptr && current->data != value) {
current = current->next;
}
if (current == nullptr) {
std::cout << "Element not found in the list\n";
return;
}
if (current->prev != nullptr) {
// Nếu current không phải là node đầu tiên
current->prev->next = current->next;
} else {
// Nếu current là node đầu tiên
head = current->next;
}
if (current->next != nullptr) {
// Nếu current không phải là node cuối cùng
current->next->prev = current->prev;
}
delete current;
}
// Các hàm khác như khởi tạo, thêm phần tử, in danh sách, v.v., cũng
cần được triển khai
};
int main() {
DoublyLinkedList<int> myList;

// Thêm các phần tử vào danh sách


// ...
// In danh sách trước khi xóa
// ...
// Xóa phần tử có giá trị 42
myList.erase(42);
// In danh sách sau khi xóa
// ...
return 0;
}
3. Lập Trình Phương Thức Thêm Một Phần Tử Vào Đầu Danh Sách
Liên Kết Kép:
#include <iostream>
template <typename T>
class Node {
public:
T data;
Node* prev;
Node* next;
Node(T value) : data(value), prev(nullptr), next(nullptr) {}
};
template <typename T>
class DoublyLinkedList {
private:
Node<T>* head;
public:
DoublyLinkedList() : head(nullptr) {}
// Hàm thêm một phần tử vào đầu danh sách
void push_front(T value) {
Node<T>* newNode = new Node<T>(value);
// Nối node mới với node đầu tiên hiện tại
newNode->next = head;
// Cập nhật con trỏ prev của node đầu tiên nếu danh sách không
rỗng
if (head != nullptr) {
head->prev = newNode;
}
// Cập nhật con trỏ đầu tiên để trỏ đến node mới
head = newNode;
}
// Các hàm khác như khởi tạo, xóa phần tử, in danh sách, v.v., cũng
cần được triển khai
};
int main() {
DoublyLinkedList<int> myList;
// Thêm các phần tử vào danh sách
// ...
// In danh sách trước khi thêm vào đầu
// ...
// Thêm phần tử mới vào đầu danh sách
myList.push_front(99);
// In danh sách sau khi thêm vào đầu
// ...
return 0;
}
Câu 4
Khái Niệm Cây và Cấu Trúc Lớp Cây Tổng Quát:

Trong cấu trúc dữ liệu, một cây là một tập hợp các nút được tổ chức
theo cách sao cho có một nút đặc biệt được gọi là "nút gốc" và mỗi nút
khác đều có một nút cha (nút mà nó kết nối đến). Mỗi nút có thể có
một hoặc nhiều nút con, và nút không có con được gọi là "nút lá".

Cấu trúc lớp cây tổng quát có thể được định nghĩa như sau:

template <typename T>


class TreeNode {
public:
T data; // Dữ liệu của nút
std::vector<TreeNode*> children; // Danh sách các nút con
TreeNode(T value) : data(value), children() {}
};
template <typename T>
class Tree {
private:
TreeNode<T>* root; // Nút gốc của cây
public:
Tree() : root(nullptr) {}
// Hàm thêm một nút mới vào cây
void addNode(T value) {
TreeNode<T>* newNode = new TreeNode<T>(value);
if (root == nullptr) {
// Nếu cây đang trống, nút mới sẽ làm nút gốc
root = newNode;
} else {
// Ngược lại, thêm nút mới vào danh sách các nút con của nút
gốc
root->children.push_back(newNode);
}
}
// Các hàm khác như duyệt cây, tìm kiếm, xóa nút, v.v., cũng cần
được triển khai
};
Lập Trình Phương Thức Thêm Một Nút Mới Vào Cây:
#include <iostream>
#include <vector>
template <typename T>
class TreeNode {
public:
T data; // Dữ liệu của nút
std::vector<TreeNode*> children; // Danh sách các nút con
TreeNode(T value) : data(value), children() {}
};
template <typename T>
class Tree {
private:
TreeNode<T>* root; // Nút gốc của cây
public:
Tree() : root(nullptr) {}
// Hàm thêm một nút mới vào cây
void addNode(T value) {
TreeNode<T>* newNode = new TreeNode<T>(value);
if (root == nullptr) {
// Nếu cây đang trống, nút mới sẽ làm nút gốc
root = newNode;
} else {
// Ngược lại, thêm nút mới vào danh sách các nút con của nút
gốc
root->children.push_back(newNode);
}
}
// Các hàm khác như duyệt cây, tìm kiếm, xóa nút, v.v., cũng cần
được triển khai
};
int main() {
Tree<int> myTree;

// Thêm các nút mới vào cây


myTree.addNode(1);
myTree.addNode(2);
myTree.addNode(3);
// Thêm nút mới vào cây (nút 4 là con của nút 2)
myTree.addNode(4);
// Thêm nút mới vào cây (nút 5 là con của nút gốc)
myTree.addNode(5);
// Các thao tác khác trên cây
// ...
return 0;
}
Câu 5
Khái Niệm Cây Nhị Phân:
Cây nhị phân là một cấu trúc dữ liệu cây mà mỗi nút có tối đa hai nút
con, được gọi là nút trái và nút phải. Các nút trái chứa giá trị nhỏ hơn
hoặc bằng giá trị của nút cha, trong khi các nút phải chứa giá trị lớn
hơn. Điều này tạo ra một cấu trúc có thứ tự, giúp trong quá trình tìm
kiếm và sắp xếp.
Lập Trình Phương Thức Thêm (Insert) Một Nút Mới Vào Cây Nhị Phân:
#include <iostream>
template <typename T>
class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinaryTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ thêm nút
TreeNode<T>* insert(TreeNode<T>* node, T value) {
if (node == nullptr) {
// Nếu cây rỗng, tạo nút mới
return new TreeNode<T>(value);
}
// Nếu giá trị nhỏ hơn giá trị của nút, thêm vào nút trái
if (value < node->data) {
node->left = insert(node->left, value);
}
// Nếu giá trị lớn hơn hoặc bằng giá trị của nút, thêm vào nút phải
else {
node->right = insert(node->right, value);
}
return node;
}
public:
BinaryTree() : root(nullptr) {}
// Hàm public để thêm nút mới
void insertNode(T value) {
root = insert(root, value);
}
// Các hàm khác như duyệt cây, tìm kiếm, xóa nút, v.v., cũng cần
được triển khai
};
int main() {
BinaryTree<int> myBinaryTree;
// Thêm các nút mới vào cây nhị phân
myBinaryTree.insertNode(5);
myBinaryTree.insertNode(3);
myBinaryTree.insertNode(7);
myBinaryTree.insertNode(2);
myBinaryTree.insertNode(4);
// Các thao tác khác trên cây nhị phân
// ...
return 0;
}
Lập Trình Phương Thức Xóa (Remove) Một Nút Trên Cây Nhị Phân:
template <typename T>
class BinaryTree {
private:
// ...

// Hàm hỗ trợ xóa nút


TreeNode<T>* remove(TreeNode<T>* node, T value) {
if (node == nullptr) {
return nullptr;
}
// Tìm nút cần xóa
if (value < node->data) {
node->left = remove(node->left, value);
} else if (value > node->data) {
node->right = remove(node->right, value);
} else {
// Nếu nút có một hoặc không có con
if (node->left == nullptr) {
TreeNode<T>* temp = node->right;
delete node;
return temp;
} else if (node->right == nullptr) {
TreeNode<T>* temp = node->left;
delete node;
return temp;
}
// Nếu nút có hai con, tìm giá trị thay thế từ cây con phải (cây
con nhỏ nhất của nút phải)
TreeNode<T>* temp = minValueNode(node->right);
// Copy giá trị của nút thay thế
node->data = temp->data;
// Xóa nút thay thế
node->right = remove(node->right, temp->data);
}
return node;
}
// Hàm hỗ trợ tìm giá trị nhỏ nhất trong cây con
TreeNode<T>* minValueNode(TreeNode<T>* node) {
TreeNode<T>* current = node;
// Lặp qua cây con trái để tìm giá trị nhỏ nhất
while (current->left != nullptr) {
current = current->left;
}
return current;
}
public:
// ...
// Hàm public để xóa nút
void removeNode(T value) {
root = remove(root, value);
}
};
int main() {
BinaryTree<int> myBinaryTree;
// Thêm các nút mới vào cây nhị phân
myBinaryTree.insertNode(5);
myBinaryTree.insertNode(3);
myBinaryTree.insertNode(7);
myBinaryTree.insertNode(2);
myBinaryTree.insertNode(4);
// Xóa một nút khỏi cây nhị phân
myBinaryTree.removeNode(3);
// Các thao tác khác trên cây nhị phân
// ...
return 0;
}
Câu 6
Ngăn Xếp (Stack):
Ngăn xếp (Stack) là một cấu trúc dữ liệu được tổ chức theo kiểu LIFO
(Last In, First Out), có nghĩa là phần tử cuối cùng được thêm vào ngăn
xếp sẽ là phần tử đầu tiên được lấy ra. Các thao tác chính trên ngăn xếp
bao gồm "đẩy" (push) để thêm một phần tử vào đỉnh ngăn xếp và "rút"
(pop) để lấy phần tử ở đỉnh ra khỏi ngăn xếp.
Cấu Trúc Dữ Liệu Stack:
+---+ +---+ +---+
| | <--- | | <--- | | <--- Top
+---+ +---+ +---+
^ ^ ^
| | |
data 1 data 2 data 3

Ví dụ trong Thực Tế về Cấu Trúc Stack:

1. Hệ Thống Gọi Điện Thoại:


 Khi bạn gọi điện thoại, số điện thoại cuối cùng bạn gọi thường là
số điện thoại được lưu trữ ở đỉnh ngăn xếp.
 Khi bạn nhấn nút "Redial", hệ thống sẽ lấy số điện thoại cuối cùng
ra khỏi ngăn xếp và gọi lại.
2. Undo/Redo Trong Ứng Dụng Văn Bản:
 Các hành động như viết, cắt, dán, thay đổi màu sắc, v.v., thường
được thực hiện thông qua một ngăn xếp.
 Khi bạn nhấn nút "Undo", hệ thống sẽ lấy hành động cuối cùng ra
khỏi ngăn xếp để hoàn tác. Ngược lại, khi bạn nhấn "Redo", hành
động đã hoàn tác sẽ được đưa lại vào ngăn xếp.

Thuật Toán Chuyển Đổi Biểu Thức Dạng Trung Tố Sang Dạng Hậu Tố:
Thuật toán chuyển đổi biểu thức dạng trung tố sang dạng hậu tố sử
dụng ngăn xếp để lưu trữ các toán tử và thực hiện các phép toán theo
thứ tự ưu tiên.

1. Tạo một ngăn xếp để lưu trữ các toán tử.


2. Duyệt qua từng phần tử trong biểu thức trung tố từ trái sang phải.
3. Nếu là toán hạng, đưa nó vào kết quả.
4. Nếu là toán tử, thì: a. Nếu ngăn xếp rỗng hoặc toán tử ở đỉnh ngăn xếp
có độ ưu tiên nhỏ hơn, đưa toán tử vào ngăn xếp. b. Ngược lại, lấy toán
tử ở đỉnh ngăn xếp ra và đưa vào kết quả. Lặp lại bước này cho đến khi
có thể đưa toán tử mới vào ngăn xếp.
5. Kết thúc, lấy toàn bộ các toán tử còn lại từ ngăn xếp và đưa vào kết
quả.

Ví Dụ Thuật Toán:

Biểu thức trung tố: (5 + 3) * 8 - 6

1. Duyệt qua từng phần tử:


 5→Theˆm vaˋo keˆˊt quả5→Theˆm vaˋo keˆˊt quả

 +→Đưa vaˋo nga˘n xeˆˊp+→Đưa vaˋo nga˘n xeˆˊp

 3→Theˆm vaˋo keˆˊt quả3→Theˆm vaˋo keˆˊt quả

 ∗→Đưa vaˋo nga˘n xeˆˊp∗→Đưa vaˋo nga˘n xeˆˊp

 8→Theˆm vaˋo keˆˊt quả8→Theˆm vaˋo keˆˊt quả

 −→Đưa vaˋo nga˘n xeˆˊp−→Đưa vaˋo nga˘n xeˆˊp

 6→Theˆm vaˋo keˆˊt quả6→Theˆm vaˋo keˆˊt quả

2. Lấy các toán tử còn lại từ ngăn xếp và đưa vào kết quả: Nga˘n xeˆˊp→−
+Nga˘n xeˆˊp→−+

Kết quả: 5 3 + 8 ∗ 6 −

Câu 7
Hàng Đợi (Queue):

Hàng đợi là một cấu trúc dữ liệu tuyến tính theo kiểu FIFO (First In, First
Out), nghĩa là phần tử đầu tiên được thêm vào hàng đợi sẽ là phần tử
đầu tiên được lấy ra. Các thao tác chính trên hàng đợi bao gồm
"enqueue" để thêm một phần tử vào cuối hàng đợi và "dequeue" để
lấy phần tử ở đầu ra khỏi hàng đợi.

Cấu Trúc Dữ Liệu Queue:


Front Rear
+---+ +---+ +---+ +---+ +---+
| | | | | | | | | |
+---+ +---+ +---+ +---+ +---+
^ ^
| |
Front Rear
1. Hệ Thống In Ấn:
 Các công việc in sẽ được đặt vào hàng đợi theo thứ tự đến. Công
việc đầu tiên đến sẽ được in đầu tiên, và sau đó là các công việc
khác theo thứ tự.
2. Hệ Thống Dịch Vụ Khách Hàng:
 Trong hệ thống dịch vụ khách hàng, các yêu cầu từ khách hàng sẽ
được đặt vào hàng đợi và được phục vụ theo thứ tự đến. Khách
hàng đến trước sẽ được phục vụ trước.

Lập Trình Thuật Toán:

#include <iostream>
#include <queue>
int main() {
// Định nghĩa một hàng đợi (queue) sử dụng thư viện STL
std::queue<int> myQueue;
// Thêm phần tử vào cuối hàng đợi (enqueue)
myQueue.push(1);
myQueue.push(2);
myQueue.push(3);
// In phần tử ở đầu hàng đợi
std::cout << "Front element: " << myQueue.front() << std::endl;

// Lấy phần tử ở đầu ra khỏi hàng đợi (dequeue)


myQueue.pop();
// In phần tử ở đầu hàng đợi sau khi dequeue
std::cout << "Front element after dequeue: " << myQueue.front() <<
std::endl;
return 0;
}
Câu 8
Cây Tìm Kiếm Nhị Phân:

Cây tìm kiếm nhị phân (BST - Binary Search Tree) là một cấu trúc dữ liệu
cây trong đó mỗi nút có tối đa hai nút con, với nút bên trái chứa giá trị
nhỏ hơn nút cha và nút bên phải chứa giá trị lớn hơn nút cha. Điều này
tạo ra một cấu trúc dữ liệu có thứ tự, giúp thực hiện tìm kiếm và
thêm/xóa phần tử một cách hiệu quả.
Cấu Trúc Lớp Cây Tìm Kiếm Nhị Phân:

Dưới đây là một định nghĩa cơ bản của một nút trong cây tìm kiếm nhị
phân và của cây tìm kiếm nhị phân:

template <typename T>


class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;

TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}


};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ thêm nút vào cây
TreeNode<T>* insert(TreeNode<T>* node, T value) {
if (node == nullptr) {
return new TreeNode<T>(value);
}
if (value < node->data) {
node->left = insert(node->left, value);
} else if (value > node->data) {
node->right = insert(node->right, value);
}
return node;
}
// Hàm hỗ trợ tìm kiếm nút trong cây
TreeNode<T>* search(TreeNode<T>* node, T value) {
if (node == nullptr || node->data == value) {
return node;
}
if (value < node->data) {
return search(node->left, value);
} else {
return search(node->right, value);
}
}

public:
BinarySearchTree() : root(nullptr) {}
// Hàm public để thêm nút vào cây
void insertNode(T value) {
root = insert(root, value);
}
// Hàm public để tìm kiếm nút trong cây
TreeNode<T>* searchNode(T value) {
return search(root, value);
}
// Các hàm khác như xóa nút, duyệt cây, v.v., cũng cần được triển
khai
};
Lập Trình Phương Thức Tìm Kiếm Trên Cây Tìm Kiếm Nhị Phân:
#include <iostream>
template <typename T>
class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ tìm kiếm nút trong cây
TreeNode<T>* search(TreeNode<T>* node, T value) {
if (node == nullptr || node->data == value) {
return node;
}
if (value < node->data) {
return search(node->left, value);
} else {
return search(node->right, value);
}
}
public:
BinarySearchTree() : root(nullptr) {}
// Hàm public để tìm kiếm nút trong cây
TreeNode<T>* searchNode(T value) {
return search(root, value);
}
// Các hàm khác như thêm nút, xóa nút, duyệt cây, v.v., cũng cần
được triển khai
};
int main() {
BinarySearchTree<int> myBST;

// Thêm các nút mới vào cây tìm kiếm nhị phân
myBST.insertNode(5);
myBST.insertNode(3);
myBST.insertNode(7);
myBST.insertNode(2);
myBST.insertNode(4);
// Tìm kiếm nút trong cây
TreeNode<int>* resultNode = myBST.searchNode(3);
if (resultNode != nullptr) {
std::cout << "Node found: " << resultNode->data << std::endl;
} else {
std::cout << "Node not found" << std::endl;
}
return 0;
}
Câu 9

Định Nghĩa Cây Tìm Kiếm Nhị Phân và Cấu Trúc Lớp:

Cây tìm kiếm nhị phân (BST - Binary Search Tree) là một cấu trúc dữ liệu
cây trong đó mỗi nút có tối đa hai nút con, với nút bên trái chứa giá trị
nhỏ hơn nút cha và nút bên phải chứa giá trị lớn hơn nút cha. Dưới đây
là cấu trúc lớp đơn giản cho cây tìm kiếm nhị phân:

template <typename T>


class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ thêm nút vào cây
TreeNode<T>* insert(TreeNode<T>* node, T value) {
if (node == nullptr) {
return new TreeNode<T>(value);
}
if (value < node->data) {
node->left = insert(node->left, value);
} else if (value > node->data) {
node->right = insert(node->right, value);
}
return node;
}
public:
BinarySearchTree() : root(nullptr) {}
// Hàm public để thêm nút vào cây
void insertNode(T value) {
root = insert(root, value);
}
// Các hàm khác như tìm kiếm nút, xóa nút, duyệt cây, v.v., cũng cần
được triển khai
};
Lập Trình Phương Thức Thêm Một Nút Mới:
#include <iostream>
template <typename T>
class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ thêm nút vào cây
TreeNode<T>* insert(TreeNode<T>* node, T value) {
if (node == nullptr) {
return new TreeNode<T>(value);
}
if (value < node->data) {
node->left = insert(node->left, value);
} else if (value > node->data) {
node->right = insert(node->right, value);
}
return node;
}
public:
BinarySearchTree() : root(nullptr) {}
// Hàm public để thêm nút vào cây
void insertNode(T value) {
root = insert(root, value);
}
// Các hàm khác như tìm kiếm nút, xóa nút, duyệt cây, v.v., cũng cần
được triển khai
};
int main() {
BinarySearchTree<int> myBST;
// Thêm các nút mới vào cây tìm kiếm nhị phân
myBST.insertNode(5);
myBST.insertNode(3);
myBST.insertNode(7);
myBST.insertNode(2);
myBST.insertNode(4);
// In cây sau khi thêm các nút
// (Phương thức in cây cần được triển khai)
// printTree(myBST.getRoot());
return 0;
}
Lập trình phương thức tìm kiếm trên tìm kiếm nhị phân.
#include <iostream>
template <typename T>
class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ tìm kiếm nút trong cây
TreeNode<T>* search(TreeNode<T>* node, T value) {
if (node == nullptr || node->data == value) {
return node;
}
if (value < node->data) {
return search(node->left, value);
} else {
return search(node->right, value);
}
}
public:
BinarySearchTree() : root(nullptr) {}
// Hàm public để thêm nút vào cây
void insertNode(T value) {
root = insert(root, value);
}
// Hàm public để tìm kiếm nút trong cây
TreeNode<T>* searchNode(T value) {
return search(root, value);
}
// Các hàm khác như xóa nút, duyệt cây, v.v., cũng cần được triển
khai
};
int main() {
BinarySearchTree<int> myBST;
// Thêm các nút mới vào cây tìm kiếm nhị phân
myBST.insertNode(5);
myBST.insertNode(3);
myBST.insertNode(7);
myBST.insertNode(2);
myBST.insertNode(4);
// Tìm kiếm nút trong cây
TreeNode<int>* resultNode = myBST.searchNode(3);
if (resultNode != nullptr) {
std::cout << "Node found: " << resultNode->data << std::endl;
} else {
std::cout << "Node not found" << std::endl;
}
return 0;
}
Câu 10

Định Nghĩa Cây Tìm Kiếm Nhị Phân và Cấu Trúc Lớp:

Cây tìm kiếm nhị phân (BST - Binary Search Tree) là một cấu trúc dữ liệu
cây trong đó mỗi nút có tối đa hai nút con, với nút bên trái chứa giá trị
nhỏ hơn nút cha và nút bên phải chứa giá trị lớn hơn nút cha. Dưới đây
là cấu trúc lớp đơn giản cho cây tìm kiếm nhị phân:

template <typename T>


class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ thêm nút vào cây
TreeNode<T>* insert(TreeNode<T>* node, T value) {
if (node == nullptr) {
return new TreeNode<T>(value);
}
if (value < node->data) {
node->left = insert(node->left, value);
} else if (value > node->data) {
node->right = insert(node->right, value);
}
return node;
}
public:
BinarySearchTree() : root(nullptr) {}
// Hàm public để thêm nút vào cây
void insertNode(T value) {
root = insert(root, value);
}
// Các hàm khác như tìm kiếm nút, xóa nút, duyệt cây, v.v., cũng cần
được triển khai
};
Lập Trình Phương Thức Thêm Một Nút Mới:
#include <iostream>
template <typename T>
class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ thêm nút vào cây
TreeNode<T>* insert(TreeNode<T>* node, T value) {
if (node == nullptr) {
return new TreeNode<T>(value);
}
if (value < node->data) {
node->left = insert(node->left, value);
} else if (value > node->data) {
node->right = insert(node->right, value);
}
return node;
}
public:
BinarySearchTree() : root(nullptr) {}
// Hàm public để thêm nút vào cây
void insertNode(T value) {
root = insert(root, value);
}
// Các hàm khác như tìm kiếm nút, xóa nút, duyệt cây, v.v., cũng cần
được triển khai
};
int main() {
BinarySearchTree<int> myBST;
// Thêm các nút mới vào cây tìm kiếm nhị phân
myBST.insertNode(5);
myBST.insertNode(3);
myBST.insertNode(7);
myBST.insertNode(2);
myBST.insertNode(4);
// In cây sau khi thêm các nút
// (Phương thức in cây cần được triển khai)
// printTree(myBST.getRoot());
return 0;
}
Lập trình phương thức xóa một nút trên cây tìm kiếm nhị phân.
#include <iostream>
template <typename T>
class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ thêm nút vào cây
TreeNode<T>* insert(TreeNode<T>* node, T value) {
if (node == nullptr) {
return new TreeNode<T>(value);
}
if (value < node->data) {
node->left = insert(node->left, value);
} else if (value > node->data) {
node->right = insert(node->right, value);
}
return node;
}
// Hàm hỗ trợ xóa nút trong cây
TreeNode<T>* remove(TreeNode<T>* node, T value) {
if (node == nullptr) {
return node;
}
// Tiếp tục tìm kiếm nút cần xóa
if (value < node->data) {
node->left = remove(node->left, value);
} else if (value > node->data) {
node->right = remove(node->right, value);
} else {
// Nếu nút có 0 hoặc 1 con
if (node->left == nullptr) {
TreeNode<T>* temp = node->right;
delete node;
return temp;
} else if (node->right == nullptr) {
TreeNode<T>* temp = node->left;
delete node;
return temp;
}
// Nếu nút có 2 con, lấy nút kế tiếp theo thứ tự tăng dần (nút
nhỏ nhất trong cây con phải)
TreeNode<T>* temp = minValueNode(node->right);
// Copy giá trị của nút kế tiếp
node->data = temp->data;
// Xóa nút kế tiếp
node->right = remove(node->right, temp->data);
}
return node;
}
// Hàm hỗ trợ tìm nút có giá trị nhỏ nhất
TreeNode<T>* minValueNode(TreeNode<T>* node) {
TreeNode<T>* current = node;
// Tiếp tục di chuyển về bên trái để tìm nút có giá trị nhỏ nhất
while (current->left != nullptr) {
current = current->left;
}
return current;
}
public:
BinarySearchTree() : root(nullptr) {}
// Phương thức public để thêm nút vào cây
void insertNode(T value) {
root = insert(root, value);
}
// Phương thức public để xóa nút khỏi cây
void removeNode(T value) {
root = remove(root, value);
}
// Các hàm khác như tìm kiếm nút, duyệt cây, v.v., cũng cần được
triển khai
};
int main() {
BinarySearchTree<int> myBST;
// Thêm các nút mới vào cây tìm kiếm nhị phân
myBST.insertNode(5);
myBST.insertNode(3);
myBST.insertNode(7);
myBST.insertNode(2);
myBST.insertNode(4);
// Xóa một nút khỏi cây
myBST.removeNode(3);
return 0;
}
Câu 11
Định Nghĩa Cây Tìm Kiếm Nhị Phân và Cấu Trúc Lớp:

Cây tìm kiếm nhị phân (BST - Binary Search Tree) là một cấu trúc dữ liệu
cây trong đó mỗi nút có tối đa hai nút con, với nút bên trái chứa giá trị
nhỏ hơn nút cha và nút bên phải chứa giá trị lớn hơn nút cha. Dưới đây
là cấu trúc lớp đơn giản cho cây tìm kiếm nhị phân:

template <typename T>


class TreeNode {
public:
T data;
TreeNode* left;
TreeNode* right;
TreeNode(T value) : data(value), left(nullptr), right(nullptr) {}
};
template <typename T>
class BinarySearchTree {
private:
TreeNode<T>* root;
// Hàm hỗ trợ thêm nút vào cây
TreeNode<T>* insert(TreeNode<T>* node, T value) {
if (node == nullptr) {
return new TreeNode<T>(value);
}
if (value < node->data) {
node->left = insert(node->left, value);
} else if (value > node->data) {
node->right = insert(node->right, value);
}
return node;
}
public:
BinarySearchTree() : root(nullptr) {}
// Hàm public để thêm nút vào cây
void insertNode(T value) {
root = insert(root, value);
}
// Các hàm khác như tìm kiếm nút, xóa nút, duyệt cây, v.v., cũng cần
được triển khai
};
Vẽ cây tìm kiếm nhị phân với các nút lần lượt có giá trị sau: 246, 123,
132, 542, 65, 354, 165, 567, 80, 188, 220, 75.
246
/ \
123 542
/ \ \
65 132 567
/ / \ \
75 354 165 ...
/ /
220 80
/
188

Trong biểu đồ trên:

 Nút gốc có giá trị là 246.


 Nút con bên trái của nút gốc có giá trị là 123, và nút con phải có giá trị
là 542.
 Tiếp tục tương tự, các nút khác sẽ được thêm vào cây theo quy tắc: giá
trị nhỏ hơn nút cha ở bên trái và giá trị lớn hơn nút cha ở bên phải.

Lưu ý rằng cây tìm kiếm nhị phân có thể có nhiều cấu trúc khác nhau
tùy thuộc vào thứ tự thêm các giá trị vào cây.
câu 12
Bảng băm (Hashtable) là một cấu trúc dữ liệu dùng để lưu trữ các cặp
khóa-giá trị. Nó hoạt động dựa trên việc ánh xạ giá trị khóa (key) sang
một vị trí trong bảng băm, giúp tìm kiếm và truy cập các giá trị một cách
nhanh chóng.

Cấu trúc bảng băm bao gồm một mảng (array) với các phần tử là các
bucket hoặc danh sách liên kết. Khi một phần tử được thêm vào bảng
băm, hàm băm (hash function) sẽ ánh xạ khóa của phần tử đó thành
một vị trí trong mảng, sau đó phần tử sẽ được lưu vào bucket tại vị trí
đó.
Phương thức tìm kiếm trên bảng băm thường dùng hàm băm để tìm vị
trí dự kiến của phần tử trong mảng, sau đó tìm kiếm trong bucket tại vị
trí đó.

Dưới đây là một cài đặt đơn giản của bảng băm và phương thức tìm
kiếm:

#include <iostream>
#include <vector>
#include <list>
template <typename K, typename V>
class HashTable {
private:
std::vector<std::list<std::pair<K, V>>> table;
int size;
int hashFunction(const K& key) {
// Một hàm băm đơn giản, có thể thay thế bằng hàm băm phức tạp
hơn
return key % size;
}
public:
HashTable(int initialSize) : size(initialSize) {
table.resize(size);
}
void insert(const K& key, const V& value) {
int index = hashFunction(key);
table[index].push_back(std::make_pair(key, value));
}
V search(const K& key) {
int index = hashFunction(key);
for (auto& pair : table[index]) {
if (pair.first == key) {
return pair.second;
}
}
// Trả về giá trị mặc định nếu không tìm thấy khóa
return V();
}
};
int main() {
HashTable<int, std::string> myHashTable(10);
myHashTable.insert(5, "Five");
myHashTable.insert(15, "Fifteen");
myHashTable.insert(25, "Twenty-Five");
std::cout << "Value for key 5: " << myHashTable.search(5) <<
std::endl;
std::cout << "Value for key 15: " << myHashTable.search(15) <<
std::endl;
std::cout << "Value for key 25: " << myHashTable.search(25) <<
std::endl;
std::cout << "Value for key 10: " << myHashTable.search(10) <<
std::endl;
return 0;
}
Câu 13
Bảng băm (Hashtable) là một cấu trúc dữ liệu dùng để lưu trữ các cặp
khóa-giá trị. Nó hoạt động dựa trên việc ánh xạ giá trị khóa (key) sang
một vị trí trong bảng băm, giúp tìm kiếm và truy cập các giá trị một cách
nhanh chóng.

Cấu trúc bảng băm bao gồm một mảng (array) với các phần tử là các
bucket hoặc danh sách liên kết. Khi một phần tử được thêm vào bảng
băm, hàm băm (hash function) sẽ ánh xạ khóa của phần tử đó thành
một vị trí trong mảng, sau đó phần tử sẽ được lưu vào bucket tại vị trí
đó.

Phương thức tìm kiếm trên bảng băm thường dùng hàm băm để tìm vị
trí dự kiến của phần tử trong mảng, sau đó tìm kiếm trong bucket tại vị
trí đó.

Vẽ cấu trúc của bảng băm với các giá trị khóa lần lượt: 564, 123, 679,
542, 65, 354, 165, 567, 80, 434. Biết rằng kích thước của bảng m = 9 và
hàm băm là hàm chia.

Dưới đây là cấu trúc của bảng băm với các giá trị khóa được ánh xạ
bằng hàm chia, và kích thước của bảng là 9:
Bảng băm (kích thước m = 9):
---------------------
0 | 354
1 | 165, 543
2 | 80, 542
3 | 123, 564
4|
5 | 65, 434
6 | 567
7|
8 | 679
9|

Trong biểu đồ trên:

 Giá trị của khóa được ánh xạ sử dụng hàm chia (key % m) để xác định vị
trí trong bảng.
 Các giá trị khóa được thêm vào các bucket tương ứng.
 Nếu có nhiều giá trị cùng ánh xạ vào một bucket, chúng sẽ được lưu
trong một danh sách liên kết hoặc một cách nào đó phù hợp.

Lưu ý rằng cấu trúc thực tế của bảng băm có thể thay đổi tùy thuộc vào
cách xử lý xung đột (collision handling), như sử dụng danh sách liên kết,
cây tìm kiếm nhị phân, hoặc các kỹ thuật xử lý khác. Trong ví dụ này, tôi
sử dụng danh sách liên kết để lưu trữ các giá trị khóa có cùng vị trí ánh
xạ.

You might also like