intTypePromotion=1
zunia.vn Tuyển sinh 2024 dành cho Gen-Z zunia.vn zunia.vn
ADSENSE

Bài giảng Cấu trúc dữ liệu và giải thuật: Cây nhị phân tìm kiếm - Nguyễn Mạnh Hiển (HKI năm 2020-2021)

Chia sẻ: Mỹ Nhân | Ngày: | Loại File: PDF | Số trang:26

38
lượt xem
3
download
 
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Bài giảng "Cấu trúc dữ liệu và giải thuật: Cây nhị phân tìm kiếm" cung cấp cho bạn đọc những kiến thức về khái niệm cây nhị phân tìm kiếm, các thao tác trên cây nhị phân tìm kiếm, một vài ví dụ sử dụng cây nhị phân tìm kiếm.

Chủ đề:
Lưu

Nội dung Text: Bài giảng Cấu trúc dữ liệu và giải thuật: Cây nhị phân tìm kiếm - Nguyễn Mạnh Hiển (HKI năm 2020-2021)

  1. Cây nhị phân tìm kiếm (Binary Search Trees) Nguyễn Mạnh Hiển hiennm@tlu.edu.vn
  2. Định nghĩa • Giả thiết các giá trị trên cây khác nhau. • Cây nhị phân tìm kiếm là cây nhị phân, trong đó với mọi nút X: − Tất cả các giá trị trên cây con trái của X nhỏ hơn X. − Tất cả các giá trị trên cây con phải của X lớn hơn X.
  3. Đây có phải là cây nhị phân tìm kiếm?
  4. Các thao tác chính • Tìm phần tử nhỏ nhất • Tìm phần tử lớn nhất • Tìm phần tử x • Chèn phần tử x • Xóa phần tử x Tất cả các thao tác trên có thời gian chạy trung bình là O(log N)  sẽ chứng minh sau.
  5. Cài đặt template // T là kiểu phần tử class BinarySearchTree { public: Hàm tạo, hàm hủy; Kiểm tra rỗng; Xóa rỗng cây; Tìm min, tìm max, tìm phần tử x; Chèn/xóa phần tử x; private: struct BinaryNode { ... }; // Kiểu của các nút BinaryNode * root; // Con trỏ tới nút gốc Các hàm trợ giúp; };
  6. Kiểu của các nút struct BinaryNode { T elem; // Phần tử BinaryNode * left; // Con trỏ tới con trái BinaryNode * right; // Con trỏ tới con phải // Hàm tạo của cấu trúc BinaryNode. BinaryNode(T x, BinaryNode * l, BinaryNode * r) { elem = x; // Khởi tạo trường phần tử left = l; // Khởi tạo trường con trỏ trái right = r; // Khởi tạo trường con trỏ phải } };
  7. Hàm tạo, hàm hủy, xóa rỗng BinarySearchTree() { root = NULL; // Ban đầu cây rỗng } ~BinarySearchTree() { makeEmpty(); // Xóa hết các nút trên cây } void makeEmpty() { // Hàm xóa rỗng cây makeEmpty(root); // Gọi hàm private trợ giúp (slide sau) } bool isEmpty() { // Hàm kiểm tra rỗng return (root == NULL); }
  8. Xóa rỗng cây có gốc t // Hàm private trợ giúp việc xóa rỗng cây, viết theo // kiểu đệ quy. void makeEmpty(BinaryNode * & t) { Có dấu & trước t vì sẽ gán giá trị mới cho t trong thân hàm. if (t == NULL) return; // Thoát ra nếu cây rỗng makeEmpty(t->left); // Xóa rỗng cây con trái makeEmpty(t->right); // Xóa rỗng cây con phải delete t; // Xóa nút gốc t = NULL; // Cây đã rỗng tức là không tồn tại gốc }
  9. Tìm phần tử nhỏ nhất // Hàm public. T findMin() { BinaryNode * v = findMin(root); // Gọi hàm private return v->elem; // Lấy ra phần tử nhỏ nhất và trả về } // Hàm private trợ giúp (dùng đệ quy). // Phần tử nhỏ nhất nằm ở nút ngoài cùng bên trái của cây. BinaryNode * findMin(BinaryNode * t) { if (t == NULL) // Cây rỗng? return NULL; if(t->left == NULL) // Nút ngoài cùng bên trái? return t; return findMin(t->left); // Tìm trên cây con trái }
  10. Tìm phần tử lớn nhất // Hàm public. T findMax() { BinaryNode * v = findMax(root); // Gọi hàm private return v->elem; // Lấy ra phần tử lớn nhất và trả về } // Hàm private trợ giúp (dùng vòng lặp thay cho đệ quy). // Phần tử lớn nhất nằm ở nút ngoài cùng bên phải của cây. BinaryNode * findMax(BinaryNode * t) { if (t != NULL) while (t->right != NULL) // Chưa đến tận cùng? t = t->right; // Đi tiếp sang bên phải return t; }
  11. Tìm phần tử x // Hàm public. bool contains(T x) { return contains(x, root); // Gọi hàm private } // Hàm private trợ giúp, tìm x trên cây có gốc t. bool contains(T x, BinaryNode * t) { if (t == NULL) // Cây rỗng tức là không tìm được return false; else if (x < t->elem) // Nếu x nhỏ hơn giá trị đang xét thì return contains(x, t->left); // tìm x ở bên trái. else if (x > t->elem) // Nếu x lớn hơn giá trị đang xét thì return contains(x, t->right); // tìm x ở bên phải. else // Gặp x return true; }
  12. Chèn phần tử Trước khi chèn 5 Sau khi chèn 5
  13. Cài đặt thao tác chèn // Hàm public (chèn x vào cây). void insert(T x) { insert(x, root); // Gọi hàm private Có dấu & } trước t vì sẽ gán giá trị mới // Hàm private trợ giúp (chèn x vào cây có gốc t). cho t trong void insert(T x, BinaryNode * & t) { thân hàm. if (t == NULL) // Cây rỗng thì tạo nút mới chứa x t = new BinaryNode(x, NULL, NULL); else if (x < t->elem) // Nếu x nhỏ hơn giá trị đang xét insert(x, t->left); // thì chèn x vào cây con trái else if (x > t->elem) // Nếu x lớn hơn giá trị đang xét insert(x, t->right); // thì chèn x vào cây con phải else // Gặp x thì không làm gì cả ; // Câu lệnh rỗng }
  14. Xóa nút lá Trước khi xóa 3 Sau khi xóa 3 Cách xóa: Chỉ đơn giản xóa nút đó.
  15. Xóa nút chỉ có một con Trước khi xóa 4 Sau khi xóa 4 Cách xóa: Trước khi xóa, cho nút cha trỏ tới nút con của nút bị xóa.
  16. Xóa nút có hai con Trước khi xóa 2 Sau khi xóa 2 Cách xóa: Thay nút bị xóa (2) bằng nút nhỏ nhất trên cây con phải (3). Sau đó, xóa nút nhỏ nhất trên cây con phải (là nút lá hoặc nút một con).
  17. Cài đặt thao tác xóa // Hàm public (xóa x khỏi cây). void remove(T x) { remove(x, root); // Gọi hàm private } // Hàm private trợ giúp (xóa x khỏi cây có gốc t). void remove(T x, BinaryNode * & t) { Có dấu & trước t vì sẽ ... // Xem code ở slide sau gán giá trị mới cho t } trong thân hàm.
  18. Cài đặt thao tác xóa (tiếp) void remove(T x, BinaryNode * & t) { if (t == NULL) return; // Thoát ra nếu cây rỗng. if (x < t->elem) // Nếu x nhỏ hơn giá trị đang xét thì remove(x, t->left); // xóa x ở cây con trái. else if (x > t->elem) // Nếu x lớn hơn giá trị đang xét thì remove(x, t->right); // xóa x ở cây con phải. else if (t->left != NULL && t->right != NULL) { // Nút 2 con t->elem = findMin(t->right)->elem; Tìm min trên cây con phải rồi remove(t->elem, t->right); đặt vào nút cần xóa. } Xóa nút min trên cây con phải. else { // Nút 1 con hoặc lá BinaryNode * old = t; // Giữ lại địa chỉ của nút cần xóa. t = (t->left != NULL) ? t->left : t->right; delete old; // Xóa. } Xác định con duy nhất (có thể là NULL trong Chú ý: t chính là liên kết từ nút trường hợp nút lá) là con trái hay con phải. } cần xóa tới nút con của nó.
  19. Phân tích thời gian chạy • Gọi n là tổng số nút trên cây. • Gọi d là độ sâu trung bình của các nút. • Thao tác xóa rỗng cây có thời gian chạy là O(n), vì có bao nhiêu nút thì sẽ phải xóa một nút bấy nhiêu lần. • Các thao tác tìm/chèn/xóa có thời gian chạy trung bình là O(d), vì sẽ diễn ra hai bước sau đây: 1. Đi từ nút gốc tới nút v, nơi diễn ra thao tác, mất thời trung bình là O(d). 2. Xử lý tại nút v chỉ mất vài thao tác cơ bản, tức là O(1). • Tiếp theo, ta sẽ chứng minh d = O(log n), và vì vậy thời gian tìm/chèn/xóa trung bình là O(log n).
  20. Chứng minh d = O(log n) (1) • Độ sâu trung bình của các nút: d = Tổng-độ-sâu-của-các-nút / Số-nút = D/n Tổng độ sâu của các nút (D) được gọi là độ dài đường đi bên trong. • Hãy tính độ dài đường đi bên trong của các cây sau: 2 2 2 6 1 3 1 5 9 3 7 0 2 4 6 8
ADSENSE

CÓ THỂ BẠN MUỐN DOWNLOAD

 

Đồng bộ tài khoản
2=>2