Bài giảng Cấu trúc dữ liệu và giải thuật: Cây AVL cung cấp cho người học các khái niệm cây AVL, đặc điểm, định nghĩa cấu trúc dữ liệu, các kỹ thuật cân bằng cây, chèn phần tử vào cây, xóa phần tử khỏi cây. Mời các bạn cùng tham khảo.
Cây AVL Nguyễn Mạnh Hiển hiennm@tlu.edu.vn Mở đầu • Khi xây dựng nhị phân tìm kiếm, ta muốn có kiểu hơn? • Ví dụ: dựng từ dãy {3, 5, 8, 20, 18, 13, 22} 13 20 13 18 20 22 18 22 Mở đầu (tiếp) • Ta muốn nhị phân tìm kiếm cân đối: − có độ sâu = log n, − cho phép chèn xóa với thời gian chạy O(log n) trường hợp • Cây AVL kiểu vậy! Cây AVL (Adelson-Velskii & Landis) • Cây AVL nhị phân tìm kiếm thỏa mãn điều kiện cân bằng: − Với nút X, chiều cao hai trái phải X sai khác không − Quy ước rỗng có chiều cao -1 18 13 20 22 Cây AVL ? Chèn xóa AVL • Thực chèn/xóa nhị phân tìm kiếm thơng thường • Sau chèn/xóa, điều kiện cân bị vi phạm: − Sửa phép xoay − Sau phép xoay, trở lại cân Ví dụ phép chèn Chèn làm điều kiện cân bị vi phạm nút Sửa phép xoay quanh nút Vi phạm điều kiện cân • Nếu điều kiện cân bị vi phạm, nút cần xoay? − Chỉ nút đường từ điểm chèn ngược gốc bị ảnh hưởng • Chỉ cần tái cân dùng phép xoay nút sâu có điều kiện cân bị vi phạm − Toàn tái cân Các trường hợp vi phạm • Giả sử nút k nơi xảy vi phạm Có trường hợp: trái-trái: chèn vào trái trái k trái-phải: chèn vào phải trái k phải-trái: chèn vào trái phải k phải-phải: chèn vào phải phải k • Hai trường hợp (chèn ngoài) tương tự nhau: − Phép xoay đơn để tái cân • Hai trường hợp (chèn trong) tương tự nhau: − Phép xoay kép để tái cân Kiểu liệu nút struct AvlNode { T elem; AvlNode * left; AvlNode * right; int height; // Chiều cao nút AvlNode(T e, AvlNode * l, AvlNode * r, int h) { elem = e; left = l; right = r; height = h; } }; // Hàm trả chiều cao nút int height(AvlNode * t) { return t == NULL ? -1 : t->height; } Phép xoay đơn (trường hợp 1) void rotateWithLeftChild(AvlNode * & k2) { AvlNode * k1 = k2->left; k2->left = k1->right; k1->right = k2; k2->height = max(height(k2->left), height(k2->right)) + 1; // Hàm max trả giá trị lớn k1->height = max(height(k1->left), k2->height) + 1; k2 = k1; } Ví dụ phép xoay đơn (1) • Chèn 3, 2, sau 4, 5, 6, vào AVL rỗng Ví dụ phép xoay đơn (2) • Chèn 4, 5: Ví dụ phép xoay đơn (3) • Chèn 6: Ví dụ phép xoay đơn (4) • Chèn 7: Phép xoay đơn không giải trường hợp khác • Đối với trường hợp 2: − Sau phép xoay đơn, nút k1 khơng cân • Ta cần dùng phép xoay kép cho hai trường hợp Phép xoay kép (trường hợp 2) • Phép xoay kép trái-phải để sửa trường hợp 2: − Đầu tiên xoay nút k1 nút k2 − Sau xoay nút k2 nút k3 • Với trường hợp 3, cách làm tương tự (xoay theo chiều ngược lại) Ví dụ phép xoay kép (1) • Chèn 16, 15 14 vào AVL sau đây: Ví dụ phép xoay kép (2) • Chèn 16, 15: Ví dụ phép xoay kép (3) • Chèn 14: Phép xoay kép (trường hợp 2) void doubleWithLeftChild(AvlNode * & k3) { rotateWithRightChild(k3->left); rotateWithLeftChild(k3); } Chèn vào AVL // Hàm private chèn x vào có gốc trỏ t void insert(T x, AvlNode * & t ) { if (t == NULL) t = new AvlNode(x, NULL, NULL, 0); else if (x < t->elem) insert(x, t->left); else if (x > t->elem) insert(x, t->right); balance(t); // Cân sau chèn } Xóa khỏi AVL // Hàm private xóa phần tử x khỏi có gốc trỏ t void remove(T x, AvlNode * & t) { if (t == NULL) return; // Thoát rỗng if (x < t->elem) remove(x, t->left); else if (x > t->elem) remove(x, t->right); else if (t->left != NULL && t->right != NULL) { // Nút t->elem = findMin(t->right)->elem; remove(t->elem, t->right); } else { // Nút AvlNode * old = t; t = (t->left != NULL) ? t->left : t->right; delete old; } balance(t); // Cân sau xóa } Cân AVL sau chèn/xóa void balance(AvlNode * & t) { if (t == NULL) return; if (height(t->left) - height(t->right) > 1) if (height(t->left->left) >= height(t->left->right)) rotateWithLeftChild(t); else doubleWithLeftChild(t); else if (height(t->right) - height(t->left) > 1) if (height(t->right->right) >= height(t->right->left)) rotateWithRightChild(t); else doubleWithRightChild(t); t->height = max(height(t->left), height(t->right)) + 1; } Bài tập Vẽ hình mơ tả q trình biến đổi AVL rỗng chèn vào giá trị sau vào cây: { 2, 1, 4, 5, 9, 3, 6, } ... remove(x, t->left); else if (x > t->elem) remove(x, t->right); else if (t->left != NULL && t->right != NULL) { // Nút t->elem = findMin(t->right )-> elem; remove(t->elem, t->right); } else { // Nút AvlNode... (height(t->left->left) >= height(t->left->right)) rotateWithLeftChild(t); else doubleWithLeftChild(t); else if (height(t->right) - height(t->left) > 1) if (height(t->right->right) >= height(t->right->left))... rotateWithLeftChild(AvlNode * & k2) { AvlNode * k1 = k 2-> left; k 2-> left = k 1-> right; k 1-> right = k2; k 2-> height = max(height(k 2-> left), height(k 2-> right)) + 1; // Hàm max trả giá trị lớn k 1-> height =