Nhược điBm KiGm tra dữ liệu đầu vào: o Chưa kiGm tra tính hợp lệ của các tham số, ví dụ: Đỉnh nhập vào phải nằm trong phạm vi từ 0 đến n-1.. Cây là một tập hợp T các phần tử gọi là n
Xây dựng kiGu danh sách STACK (hoặc QUEUE) Và ứng dụng vào bài toán duyệt đồ thị theo chiều sâu (hoặc chiều rộng).
- Đồ thị là gF? Đồ thị là một tập hợp các đỉnh (vertices) và các cạnh (edges) nối các đỉnh lại với nhau.
Thuật toán tìm kiếm theo chiều sâu (DFS) là một phương pháp duyệt qua cấu trúc dữ liệu dạng cây hoặc đồ thị Thuật toán này bắt đầu từ một nút gốc (có thể chọn bất kỳ nút nào làm nút gốc trong trường hợp đồ thị) và tiến hành kiểm tra từng nhánh một cách sâu sắc trước khi quay lại.
- STACK hoặc QUEUE được triGn khai dưới dạng danh sách liên kết.
- Ứng dụng: Duyệt đồ thị theo chiều sâu (DFS) hoặc chiều rộng (BFS).
1.3 Phân tích và đánh giá
1 Khởi tạo và quản lý đồ thị
khoi_tao(int dinh): o Tạo danh sách kề đG lưu đồ thị với n đỉnh. o Độ phức tạp thời gian: O(n) đG khởi tạo danh sách kề trống.
Hàm `them_canh(int nguon, int dich)` cho phép thêm một cạnh giữa hai đỉnh nguồn và đích vào danh sách kề Đối với đồ thị vô hướng, hàm này sẽ thêm cạnh theo cả hai chiều Độ phức tạp thời gian của việc thêm mỗi cạnh là O(1).
Cách hoạt động của thuật toán này là sử dụng ngăn xếp để thay thế cho đệ quy trong tìm kiếm theo chiều sâu (DFS) Bắt đầu từ một đỉnh khởi đầu, thuật toán sẽ lần lượt duyệt các đỉnh kề chưa được thăm Các đỉnh kề sẽ được thêm vào ngăn xếp theo thứ tự ngược lại, đảm bảo thứ tự duyệt đúng như mong muốn.
Độ phức tạp thời gian: o Duyệt qua mỗi đỉnh một lần và kiGm tra tất cả các cạnh của nó → O(V+E), với:
3 Tải và lưu đồ thị từ/đến tệp
Hàm tai_tu_tep(const string& ten_tep) có chức năng đọc danh sách kề từ tệp tin Độ phức tạp thời gian của hàm này là O(V+E), trong đó V là số đỉnh và E là số cạnh, khi duyệt qua từng dòng để tạo đồ thị.
luu_vao_tep(const string& ten_tep): o Ghi danh sách kề vào tệp. o Độ phức tạp thời gian: O(V+E): Duyệt qua toàn bộ danh sách kề đG ghi vào tệp.
hien_thi_dinh(): o Liệt kê các đỉnh từ 0 đến n-1. o Độ phức tạp thời gian: O(n).
1 Độ phức tạp tổng thB
Thời gian: o Tải và lưu đồ thị: O(V+E). o Thêm cạnh: O(1) mỗi lần. o DFS: O(V+E) mỗi lần.
Không gian: o Sử dụng danh sách kề: O(V+E). o Biến phụ trong DFS (ngăn xếp, mảng đánh dấu): O(V).
Các thao tác cơ bản trong đồ thị như thêm cạnh, lưu và tải đồ thị, cũng như duyệt theo chiều sâu (DFS) đều có độ phức tạp tối ưu O(V+E) nhờ vào cấu trúc danh sách kề Bên cạnh đó, việc sử dụng ngăn xếp thay vì đệ quy trong DFS giúp ngăn chặn lỗi tràn ngăn xếp khi làm việc với các đồ thị lớn.
Khả năng tái sử dụng: Các hàm được chia nhỏ, có tính độc lập, dễ bảo trF và mở rộng.
Tích hợp tệp tin: Cho phép lưu trạng thái đồ thị và tái sử dụng sau, thuận tiện cho việc nhập/xuất dữ liệu.
KiGm tra dữ liệu đầu vào: o Chưa kiGm tra tính hợp lệ của các tham số, ví dụ:
Đỉnh nhập vào phải nằm trong phạm vi từ 0 đến n-1.
Số lượng cạnh và các cạnh phải hợp lệ.
Xử lý đồ thị có hướng: Mặc định là đồ thị vô hướng Nếu cần xử lý đồ thị có hướng, cần sửa đổi cấu trúc thêm cạnh.
HiGn thị đồ thị: Chưa có phương thức hiGn thị toàn bộ danh sách kề đG người dùng kiGm tra.
Xây dựng cấu trúc dữ liệu cây nhị phân tFm kiếm cho kiGu cấu trúc dữ liệu tự chọn
(không chọn kiGu SINH VIEN) Thực hiện các thao tác sau:
+ Thêm 1 phần tử vào cây (Dùng đệ quy và không đệ quy)
+ Xóa một phần tử khỏi cây (Dùng đệ quy và không đệ quy)
+ Tính chiều cao của cây (Chiều cao là số nút trên đường đi dài nhất từ gốc đến lá của cây) (Dùng đệ quy)
Cây là một cấu trúc dữ liệu bao gồm các nút, trong đó có một nút đặc biệt gọi là nút gốc Các nút còn lại được tổ chức thành các tập rời nhau theo quan hệ phân cấp, với mỗi tập Ti cũng là một cây Mỗi nút ở cấp i sẽ quản lý một số nút ở cấp i+1, tạo thành mối quan hệ cha – con giữa các nút.
2 Cây nhị phân là gF? Cây nhị phân (binary tree) là một cấu trúc dữ liệu cây mà mỗi nút có nhiều nhất hai nút con.
Cây tìm kiếm nhị phân (Binary Search Tree - BST) là một cấu trúc dữ liệu trong đó mỗi nút có tối đa hai nút con, với nút con bên trái chứa các giá trị nhỏ hơn nút cha và nút con bên phải chứa các giá trị lớn hơn Điều này giúp cho việc tìm kiếm, chèn và xóa các giá trị trong cây trở nên hiệu quả hơn.
- Cây con bên trái của một nút có khóa (key) nhỏ hơn hoặc bằng giá trị khóa của nút cha (của cây con này).
- Cây con bên phải của một nút có khóa lớn hơn hoặc bằng giá trị khóa của nút cha (của cây con này).
- Xây dựng cấu trúc cây nhị phân tFm kiếm (BST) cho kiGu dữ liệu tự chọn (ví dụ: kiGu Sách).
Để thêm một phần tử vào cây, có thể sử dụng cả phương pháp đệ quy và không đệ quy Khi cần xóa một phần tử khỏi cây, cũng có thể áp dụng cả hai phương pháp này Đặc biệt, để tính chiều cao của cây, ta sử dụng phương pháp đệ quy, trong đó chiều cao được xác định là số nút trên đường đi dài nhất từ gốc đến lá của cây.
1.3 Phân tích và đánh giá
1 Thêm sản phẩm vào cây
Phương pháp đệ quy: addBookRecursive()
1 Nếu cây rỗng, tạo một nút mới và gán vào cây.
So sánh maSP của sản phẩm mới với maSP của nút hiện tại.
Nếu maSP nhỏ hơn, gọi đệ quy thêm vào cây con trái.
Nếu maSP lớn hơn, gọi đệ quy thêm vào cây con phải.
Nếu trùng maSP, thông báo thêm thất bại.
Phương pháp không dùng đệ quy: addBookNotRecursive()
1 Tạo một nút mới từ sản phẩm cần thêm.
2 Nếu cây rỗng, gán nút mới làm gốc.
Nếu maSP nhỏ hơn nút hiện tại, di chuyGn đến cây con trái.
Nếu maSP lớn hơn, di chuyGn đến cây con phải.
Nếu trùng maSP, thông báo thêm thất bại.
4 Gắn nút mới vào vị trí lá thích hợp.
2 Xóa sản phẩm khỏi cây
Phương pháp đệ quy: deleteBookRecursive()
1 Duyệt cây đG tFm nút cần xóa:
Nếu maSP nhỏ hơn, tFm ở cây con trái.
Nếu maSP lớn hơn, tFm ở cây con phải.
2 Nếu tFm thấy nút cần xóa:
Trường hợp nút là lá: Xóa trực tiếp.
Trường hợp có một con: Gắn con vào vị trí của nút cần xóa.
Trong trường hợp có hai con, cần xác định nút thay thế, đó là nút nhỏ nhất ở cây con phải hoặc lớn nhất ở cây con trái Sau khi tìm được nút thay thế, tiến hành thay thế giá trị của nút cần xóa và cuối cùng là xóa nút thay thế.
3 Nếu không tFm thấy sản phẩm, thông báo lỗi.
Phương pháp không dùng đệ quy: deleteBookNotRecursive()
1 Duyệt cây bằng vòng lặp đG tFm nút cần xóa và nút cha của nó.
2 Xử lý các trường hợp xóa tương tự như phương pháp đệ quy:
Nút cần xóa là lá.
Nút cần xóa có một con.
Nút cần xóa có hai con.
3 Cập nhật liên kết của nút cha nếu cần.
4 Nếu không tFm thấy sản phẩm, thông báo lỗi.
3 Tính chiều cao cây: HighTree()
1 Nếu cây rỗng, trả về 0.
2 Tính chiều cao của cây con trái và cây con phải.
3 Trả về 1 + chiều cao lớn hơn trong hai cây con.
Cấu trúc của hệ thống được thiết kế rõ ràng, hỗ trợ cả thao tác đệ quy và không đệ quy, giúp quản lý dữ liệu cây nhị phân một cách hiệu quả Bên cạnh đó, giao diện người dùng đơn giản cũng tạo điều kiện thuận lợi cho người sử dụng.
Cây nhị phân thông thường có thể trở thành cây "một nhánh" khi dữ liệu không được cân đối, dẫn đến hiệu suất giảm xuống O(n) Để cải thiện hiệu suất, nên xem xét sử dụng cây AVL hoặc cây RED-BLACK, vì chúng hỗ trợ cân bằng cây tốt hơn.
Viết một ứng dụng cho phép người dùng nhập vào một chuỗi biểu thức toán học và tính toán giá trị của biểu thức đó Để thực hiện điều này, bạn có thể tham khảo bài toán Balan ngược để có được phương pháp giải quyết hiệu quả.
Ngăn xếp (stack) là một cấu trúc dữ liệu quan trọng, đặc biệt liên quan đến cơ chế hoạt động của đệ quy Để hiểu cách hàm đệ quy hoạt động, cần nắm rõ nguyên tắc hoạt động của ngăn xếp, đó là Last In First Out (LIFO), nghĩa là phần tử được thêm vào cuối cùng sẽ được lấy ra đầu tiên.
- Nhập biGu thức dạng trung tố (Infix), chuyGn sang hậu tố (Postfix)
- Tính giá trị biGu thức hậu tố.
3.3 Phân tích và đánh giá
1 Duyệt từng ký tự trong biGu thức
Nếu ký tự là dấu cách: Bỏ qua.
Nếu ký tự là (: o Đưa vào ngăn xếp st1.
Nếu ký tự là số: Đọc toàn bộ số nguyên liên tiếp, chuyGn thành kiGu ll, và đưa vào ngăn xếp st2.
Nếu ký tự là ): Thực hiện lần lượt các phép toán trong st1 cho đến khi gặp (:
1 Lấy hai toán hạng trên cùng của st2.
2 Lấy toán tử trên cùng của st1.
3 Thực hiện phép tính, kết quả đưa lại vào st2.
Nếu ký tự là toán tử (+, -, *, /): Xử lý ưu tiên toán tử bằng cách:
1 Trong khi st1 không rỗng và ưu tiên của toán tử hiện tại không cao hơn toán tử trên đỉnh st1:
Lấy hai toán hạng từ st2.
Lấy toán tử từ st1.
Thực hiện phép tính và đưa kết quả lại vào st2.
Loại bỏ toán tử khỏi st1.
2 Đưa toán tử hiện tại vào st1.
Sau khi duyệt hết biGu thức, nếu st1 còn toán tử: Lặp lại việc tính toán như trên cho đến khi st1 rỗng.
Giá trị cuối cùng trong st2 là kết quả của biGu thức.
Duyệt biGu thức bằng cách đẩy 3 vào st2, sau đó đẩy + vào st1 Tiếp theo, đẩy 5 vào st2 và đẩy * vào st1 Khi gặp dấu (, ta đẩy vào st1, tiếp tục đẩy 2 vào st2 và đẩy - vào st1 Đẩy 8 vào st2, khi gặp dấu ), thực hiện phép tính 2 - 8 = -6 và đẩy kết quả -6 vào st2, đồng thời loại bỏ dấu (.
2 Xử lý phép nhân: o Thực hiện 5 * -6 = -30, đẩy kết quả -30 vào st2.
3 Xử lý phép cộng: o Thực hiện 3 + -30 = -27, đẩy kết quả -27 vào st2.
1 Độ phức tạp thời gian
Duyệt qua từng ký tự trong biGu thức đúng 1 lần → O(n), với n là độ dài biGu thức.
Trong quá trFnh xử lý, mỗi toán tử hoặc toán hạng chỉ được đẩy vào hoặc lấy ra khỏi ngăn xếp một lần → O(n).
2 Độ phức tạp không gian
Sử dụng hai ngăn xếp: o st1 có kích thước tối đa là số toán tử → O(n). o st2 có kích thước tối đa là số toán hạng → O(n).
Hiệu quả: Tính toán chính xác biGu thức trung tố bất kỳ, bao gồm cả biGu thức có ngoặc và độ ưu tiên toán tử.
Tổng quát: Hỗ trợ các phép toán cơ bản và các số nguyên có độ dài tùy ý.
Giới hạn kiGu dữ liệu: Chỉ hỗ trợ số nguyên, không hỗ trợ số thực.
Cố định toán tử: Không dễ dàng mở rộng cho các toán tử khác hoặc các hàm toán học (như sin, cos, log).
Viết một phần mềm từ điGn tiếng Anh đơn giản (không cần giao diện đồ họa). Gợi ý:
- Dữ liệu có thG được lưu trữ theo một trong 2 cách:
+ Cách 1: Lưu trữ trên các bảng băm trong bộ nhớ chính.
+ Cách 2 (được đánh giá cao hơn): Lưu trữ trên tập tin nhị phân.
Ví dụ: Nhập vào: school
Kết quả: Trường học (danh từ)
Dữ liệu được lưu chữ theo cách 1: Lưu trữ trên các bảng băm trong bộ nhớ chính.
4.1 Mô Tả Phương Pháp Lưu Trữ
1 Cấu trúc dữ liệu sử dụng: Bảng băm (hash table) với các danh sách liên kết (linked list) đG xử lý va chạm (collision handling).
Hàm băm (hashFunction) là một thuật toán chuyển đổi chuỗi ký tự từ tiếng Anh thành chỉ số trong bảng băm Thuật toán này sử dụng phương pháp băm đơn giản, kết hợp phép nhân và phép cộng với các số nguyên tố để tạo ra chỉ số duy nhất cho mỗi chuỗi ký tự.
Trong bảng băm, mỗi vị trí đều có một danh sách liên kết để lưu trữ các từ và nghĩa của chúng Điều này giúp xử lý các trường hợp va chạm khi nhiều từ được ánh xạ đến cùng một vị trí, đảm bảo tính chính xác và hiệu quả trong việc truy xuất dữ liệu.
- Quản lý bộ nhớ: Dữ liệu được lưu trực tiếp trên bộ nhớ chính (RAM) trong suốt thời gian chương trFnh chạy.
4.2 Chức Năng Của Phần Mềm
Để khởi tạo bảng băm, cần sử dụng một mảng có kích thước cố định là 1009, đây là một số nguyên tố, để lưu trữ các danh sách liên kết Tất cả các vị trí trong mảng sẽ được gán giá trị là nullptr ban đầu.
Để thêm từ vào từ điển, bạn cần sử dụng hàm băm để xác định chỉ số lưu trữ Nếu vị trí chỉ số này không chứa dữ liệu, hãy tạo một nút mới trong danh sách liên kết tại vị trí đó.
Để tra cứu nghĩa của từ, đầu tiên, xác định vị trí trong bảng băm bằng hàm băm Sau đó, duyệt qua danh sách liên kết tại vị trí đó để tìm từ cần tra cứu Nếu tìm thấy, trả về nghĩa của từ; nếu không, thông báo lỗi sẽ được hiển thị.
4 HiBn thị tất cả các từ (display): o Duyệt qua tất cả các vị trí trong bảng băm. o Xuất danh sách từ và nghĩa tại mỗi vị trí.
5 Giải phóng bộ nhớ (freeHash): o Duyệt qua tất cả các danh sách liên kết trong bảng băm. o Giải phóng từng nút đG tránh rò rỉ bộ nhớ.
6 Tải dữ liệu từ tệp văn bản (load_file): o Đọc dữ liệu từ tệp định dạng văn bản (dạng word : meaning). o Chèn từng từ và nghĩa vào bảng băm.
Hiệu suất cao: TFm kiếm, thêm, hoặc xóa một từ trong bảng băm có độ phức tạp trung bFnh là O(1).
Dễ dàng triGn khai: Chương trFnh chỉ cần lưu dữ liệu trên bộ nhớ chính, không phải quản lý tệp phức tạp.
Thời gian thực: Tra cứu từ nhanh chóng, phù hợp cho các ứng dụng yêu cầu hiệu suất cao.
Khi dữ liệu từ điển trở nên quá lớn, bảng băm có thể tiêu tốn nhiều RAM, dẫn đến khó khăn cho các hệ thống có tài nguyên hạn chế.
Dữ liệu không được lưu trữ lâu dài và chỉ tồn tại trong suốt thời gian chương trình hoạt động Khi chương trình bị tắt, mọi dữ liệu sẽ bị mất và cần phải tải lại từ tệp văn bản.
Hiệu suất giảm với bảng băm đầy: Khi số lượng từ lớn hơn kích thước bảng băm, tỷ lệ va chạm tăng cao, làm giảm hiệu suất.
Ví dụ: TriBn khai Từ ĐiBn Tiếng Anh - Việt bằng Cách 1
Hàm băm đN sử dụng
1 Mỗi ký tự trong từ (key) sẽ được chuyGn thành mN ASCII.
Hàm băm bắt đầu với giá trị hash bằng 0 và lặp qua từng ký tự trong chuỗi Mỗi ký tự sẽ được nhân với 31, một số nguyên tố, và cộng vào giá trị hiện tại của hash Hàm băm được định nghĩa như sau: `unsigned int hashFunction(const string& key) { unsigned int hash = 0; for (char ch : key) { hash = (hash * 31 + ch) %`.
TABLE SIZE o Cộng mN ASCII của ký tự hiện tại. o Lấy dư (% TABLE_SIZE) đG đảm bảo chỉ số luôn nằm trong phạm vi
3 Kết quả cuối cùng là chỉ số trong bảng băm.
Bảng từ, cách tính hàm băm và chỉ số băm
Xử lý hàm băm khi bị trùng
Tạo danh sách liên kết: hello -> hello2 -> halo -> hallo -> NULL