Cây nhị phân và Ứng dụng trong môn Cấu trúc dữ liệu và giải thuật Nội dụng: - Khái niệm, các loại cây nhị phân, tính chất - Biểu diễn cây nhị phân (biểu thức, số học, chữ, ....) bằng mảng, cấu trúc liên kết, các trường hợp suy biến - Trình bày giải thuật bằng mã giả tựa C++, phép tạo cây - tạo nút, duyệt cây - trước - giữa - sau, thêm, sửa, xoá. - Ứng dụng vào các bài toán như biểu diễn biểu thức, cây nhị phân tìm kiếm, BST cân bằng, cây AVL, các kỹ thuật quay lui và thuật giải
Trang 2CÂY NHỊ PHÂN
Trang 3Khái niệm: Cây (tree) là 1 tập hợp hữu hạn các nút trong đó có 1 nút đặc
biệt gọi là nút gốc (root) Giữa các nút có 1 quan hệ phân cấp gọi là “quan
hệ cha con”
Định nghĩa đệ quy:
Một nút là 1 cây Nút đó cũng là gốc của cây đó
Nếu n là 1 nút và T1, T2, …, Tk là các cây với n1, n2, …, nk lần lượt là các gốc, thì 1 cây mới T sẽ được tạo lập bằng cách cho n trở thành cha của các nút n1, n2, …, nk; nghĩa là n là gốc còn T1, T2, …, Tk là các các
cây con (subtrees) của gốc Lúc đó n1, n2, …, nk là con của nút n
Quy ước: Cây không có nút nào ta gọi là cây rỗng (null tree)
1.KHÁI NIỆM
Trang 4
T2
Trang 5 Chiều cao (height) hay chiều sâu
(depth) của một cây là số mức lớn
nhất của nút có trên cây
1
2
3
4
Trang 8Biểu diễn lịch thi đấu
Trang 10( A / B + C )*( D – E )
*
‒
ED
+
C/
BA
Trang 112.1 Khái niệm và tính chất
2.2 Biểu diễn cây nhị phân
2.3 Phép duyệt cây nhị phân
2 CÂY NHỊ PHÂN
Trang 122.1 Khái niệm và tính chất
Khái niệm: Cây nhị phân là một dạng quan trọng của cấu trúc cây Mọi nút
trên cây chỉ có tối đa hai nhánh con Với một nút, ta phân biệt cây con trái và cây con phải của cây Cây nhị phân là cây có tính đến thứ tự của các nhánh con
A
B C
A
C B
A
C B
Trang 13Các cây nhị phân suy biến
3 4
5
Cây zíc-zắc
Trang 14Các cây nhị phân hoàn chỉnh và đầy đủ
1
3 2
Cây nhị phân hoàn chỉnh (complete binary tree) : Nếu chiều cao của cây
là h thì mọi nút có mức < h - 1 đều có đúng 2 nút con
Cây nhị phân đầy đủ (full binary tree) : Mọi nút có mức <= h - 1 đều có đúng 2 nút con
1
3 2
Complete binary tree Full binary tree
Trang 152.1 Khái niệm và tính chất
Tính chất:
Trong các cây nhị phân có cùng số lượng nút thì cây nhị phân suy biến
có chiều cao lớn nhất, cây nhị phân hoàn chỉnh có chiều cao nhỏ nhất
Số lượng tối đa các nút trên mức i của cây nhị phân là , tối thiểu là 1 (i
Trang 162.1 Biểu diễn cây nhị phân
Biểu diễn bằng mảng
Biểu diễn bằng cấu trúc liên kết
Trang 17Biểu diễn bằng mảng
Nếu có một cây nhị phân đầy đủ, ta đánh số các
nút trên cây theo thứ tự từ mức 1 trở đi, hết mức
này đến mức khác từ trái sang phải đối với các nút
3
Trang 18Biểu diễn bằng mảng
A B
C D
Trường hợp cây nhị phân không đầy đủ:
1 Thêm một số nút giả để được cây nhị
phân đầy đủ và gán những giá trị đặc
biệt cho những nút này
2 Hoặc thêm một mảng phụ để đánh dấu
những nút nào là nút giả mà ta thêm
vào
Trang 19Biểu diễn bằng mảng
A B
C D
nhớ vì có thể sẽ phải thêm rất nhiều nút
giả thì mới được cây nhị phân đầy đủ
Trang 20Biểu diễn bằng cấu trúc liên kết
Khi biểu diễn cây nhị phân bằng cấu trúc liên kết, mỗi nút (node) là một bản ghi (record) gồm ba trường:
Trường Info: chứa giá trị lưu tại nút đó
Trường Left: Chứa liên kết (con trỏ) trỏ tới cây nhị phân bên trái của node
Trường Right: Chưa liên kết (con trỏ) trỏ tới cây nhị phân bên phải của node
Nếu một cây nhị phân trống, nó sẽ được biểu diễn thành con trỏ NULL
Trang 21Biểu diễn bằng cấu trúc liên kết
Cấu trúc node của cây:
Khai báo node bằng cấu trúc liên kết (con trỏ):
struct Tnode {
char word [ 20 ];
struct Tnode * left ;
struct Tnode * right ;
Trang 22Biểu diễn bằng cấu trúc liên kết
A
E
Đối với cây ta chỉ cần quan
tâm giữ lại nút gốc (root), bởi
từ nút gốc, đi theo các hướng
liên kết left, right ta có thể
duyệt được mọi nút khác
Trang 23MakeNodeNode* makeNode( char * word ){
// Phân bổ( bộ nhớ cho nút mới
Node * newNode = NULL ;
newNode = ( Node *)malloc( sizeof ( Node ));
// Kiể( m tra câ2 p phát bộ nhớ có thành cổng
newNode -> left = NULL ;
newNode -> right = NULL ;
strcpy( newNode -> word , word );
}
return newNode ;// Tra ( lại con tro ( để2 n địa chỉ ( cu (a nút mới }
Trang 24InsertNode * Insert( Node * tree ){
char word [ 20 ] = "a" ;
//Nể2 u word == "~" thì khổng nhập nút tiể2 p theo
if (strcmp( word , "~" )== 0 ) return NULL ;
tree = makeTreeNode( word );
printf( "Input left child of %s : " , word );
tree -> left = Insert( tree -> left );
printf( "Input right child of %s : " , word );
tree -> right =Insert( tree -> right );
return tree ;
}
Trang 25Tính số nút và chiều cao của cây
// Hàm tính sổ2 lượng nút cu (a cây
int countNodes( Node * tree ) {
if ( tree == NULL ) return 0 ;
else {
int ld = countNodes( tree -> left );
int rd = countNodes( tree -> right );
return 1 + ld + rd ;
}
}
// Hàm tính chiểI u cao hay độ sâu cu (a cây
int depth( Node * tree ) {
if ( tree == NULL ) return 0 ;
int ld = depth( tree -> left );
int rd = depth( tree -> right );
return 1 + ( ld > rd ? ld : rd );
}
1
3 2
Trang 26Loại bỏ cây
void freeTree( Node * tree )
{
if ( tree == NULL ) return ;
// Loại bo ( cây bển trái
freeTree( tree -> left );
// Loại bo ( cây bển pha (i
freeTree( tree -> right );
Trang 27Phép duyệt cây
Định nghĩa: Phép xử lý các nút trên cây mà ta gọi chung là phép thăm
(Visit) các nút một cách hệ thống sao cho mỗi nút chỉ được thăm một lần gọi là phép duyệt cây
Nếu 1 nút không có nút con trái (hoặc phải) thì liên kết Left (Right) của nút đó được liên kết tới 1 nút đặc biệt là NULL
Nếu cây rỗng thì nút gốc của cây đó được gán bằng NULL
Trang 28Phép duyệt cây
Thứ tự trước (Preorder) NLR
‑ Thăm nút (Visit a node)
‑ Thăm cây con trái theo thứ tự trước (Visit left subtree)
‑ Thăm cây con phải theo thứ tự trước (Visit right subtree)
Thứ tự giữa (Inorder) LNR
‑ Thăm cây con trái theo thứ tự trước (Visit left subtree)
‑ Thăm nút (Visit a node)
‑ Thăm cây con phải theo thứ tự trước (Visit right subtree)
Thứ tự sau (Postorder) LRN
‑ Thăm cây con trái theo thứ tự trước (Visit left subtree)
‑ Thăm cây con phải theo thứ tự trước (Visit right subtree)
‑ Thăm nút (Visit a node)
Trang 32Nhận xét
Ta thấy tổ chức cây theo cấu trúc liên kết tỏ ra thích hợp hơn cách tổ chức bằng mảng 1 chiều
Tuy nhiên, cũng có những nhược điểm:
Tốc độ xử lý chậm hơn do chỉ có nút gốc của cây có thể được truy cập trực tiếp còn các nút khác chỉ được truy cập sau khi đã qua 1 số thao tác duyệt cây
Tốn bộ nhớ hơn do ngoài trường info phải có thêm trường left và right
ở mỗi nút để lưu trữ địa chỉ nút con trái và nút con phải của nút đó
Trang 333.1 Cây biểu diễn biểu thức (Expression Tree) 3.2 Cây nhị phân tìm kiếm (Binary Search Tree) 3.3 Cây BST cân bằng hoàn toàn
3.4 Cây AVL (Adelson-Velsky và Landis)
3.ỨNG DỤNG
Trang 34Cây biểu diễn biểu thức
Cây biểu thức là một cây nhị phân, trong
đó mỗi nút nhánh tương ứng với toán tử
và mỗi nút lá tương ứng với toán hạng
+
3 /
2 10
Trang 35Cây biểu diễn biểu thức
Bước 1: Input biểu thức trung tố, chuẩn hoá trung tố và chuyển sang hậu tố
(LRN)
Bước 2: Khởi tạo một Stack rỗng dùng để chứa các nút của cây
Bước 3: Đọc lần lượt các phần tử của biểu thức LRN từ trái qua phải, với mỗi
phần tử đó:
Tạo ra một nút mới newNode chứa phần tử mới đọc được
Nếu phần tử này là toán tử, lấy từ Stack ra hai nút ( theo thứ tự x và y), sau đó đem liên kết phải của newNode trỏ đến x, đem liên kết trái của newNode trỏ đến y
Đẩy newNode vào Stack
Bước 4: Sau khi kết thúc bước 2 thì toàn bộ biểu thức được đọc xong, trong Stack
chỉ còn duy nhất một phần tử, phần tử đó chính là gốc của cây nhị phân biểu diễn biểu thức
Trang 36Xây dựng cây biểu diễn biểu thức
Bước 1:
Biểu thức LNR: (2+3)/5
Biểu thức LRN: 2 3 + 5 /
Đọc Xử lý stack exp
2 Input “2” vào chuỗi exp ( 2
+ “+” có priority ≥ “(“ ở đỉnh Stack, đẩy “+” vào Stack (+ 2
3 Input “3” vào chuỗi exp (+ 2 3
/ Stack đang rỗng, đẩy “/” vào Stack / 2 3 +
5 Input “5” vào chuỗi exp / 2 3 + 5
Hết Pop các phần tử còn lại trong Stack đưa vào chuỗi exp 2 3 + 5 /
Trang 375 /
Các bước còn lại:
Trang 38Xây dựng cây biểu diễn biểu thức
void buildExpTree <Biể( u thức exp> {
Trang 39Tính toán cây biễu diễn biểu thức
double evaluationNode( Node * root ){
if ( root == NULL ) return 0 ;
// Nể2 u root là nút lá, return vểI giá trị kiể( u double
if ( root -> left == NULL && root -> right == NULL )
return ( double )atof( root -> data );
// Tính cây con bển trái
double leftval = EvaluationNode( root -> left );
// Tính cây con bển pha (i
double rightval = EvaluationNode( root -> right );
// Kiể( m tra toán tư( rổI i tính toán
if (* root -> data == '+' ) return leftval + rightval ;
if (* root -> data == '-' ) return leftval - rightval ;
if (* root -> data == '*' ) return leftval * rightval ;
if (* root -> data == '/' ) return leftval / rightval ; return pow( leftval , rightval );
}
Trang 40Độ phức tạp tính toán của evaluationNode
Cách 1: Vì tính toán giá trị biểu thức, ta phải
duyệt qua tất cả các node, mỗi node một lần
(Tương tự các phép duyệt cây)
Vậy độ phức tạp tính toán là:
+
3 2
/
5 /
Trang 41Độ phức tạp tính toán của evaluationNode
Trang 42Độ phức tạp tính toán của evaluationNode
Tính giá trị của cây, ta phải tính giá trị của cây con bên trái (left child) và cây con bên phải(right child):
double leftval = EvaluationNode( root -> left );
double rightval = EvaluationNode( root -> right );
Ta có n-1 node trừ root đã xét
Giả sử trường hợp trung bình: Left child có (n-1)/2 nút con ,Right child có (n-1)/2 nút con
Vậy ,
Dễ thấy với thoả mản
Vậy theo định lý Master
Trang 433.2 Cây nhị phân tìm kiếm
Khái niệm: Cây tìm kiếm nhị phân(Binary Search Tree, viết
tắt: BST) là một cây nhị phân và có thêm các ràng buộc sau đây:
1 Giá trị của tất cả các Node ở cây con bên trái <= giá trị của Node
Trang 4410 3
7 4
8
10 3
7 2
3.2 Cây nhị phân tìm kiếm
Trang 453.2 Cây nhị phân tìm kiếm
Cây tìm kiếm nhị phân là một cấu trúc dữ liệu hiệu quả cho phép chúng
ta xây dựng nên một danh sách mà dữ liệu trên đó được sắp xếp:
Nó được gọi là cây tìm kiếm nhị phân vì nó có thể được sử dụng để tìm kiếm sự hiện diện của một phần tử trong thời gian O(log (n))
Trong trường hợp xấu nhất, cây có thể bị suy biến thành một danh sách liên kết đơn với các thao tác có độ phức tạp O(n)
Kết luận: Để có độ phức tạp giải thuật luôn là O(lg(n)) cần có cải tiến
cấu trúc của cây BST: cây nhị phân tìm kiếm cân bằng hoàn toàn, cây AVL,
Trang 463.3 BST cân bằng hoàn toàn
Khái niệm: Cây nhị phân tìm kiếm cân bằng hoàn toàn là cây nhị
phân tìm kiếm (BST) mà tại mỗi nút của nó, số nút của cây con trái
chênh lệch không quá 1 so với số nút của cây con phải
Đánh giá:
Nếu cây cân đối thì việc tìm kiếm sẽ nhanh
Đối với cây cân bằng hoàn toàn có N nút, trong trường hợp xấu nhất chỉ phải tìm qua log2N phần tử
Trang 4710 3
3.3 BST cân bằng hoàn toàn
8
10 4
7 3
1
9
Trang 483.3 BST cân bằng hoàn toàn
Nếu thêm khóa 11 vào cây BST
thì cây không còn cân bằng hoàn
Trang 503.4 Cây AVL
Khái niệm: Cây nhị phân tìm kiếm cân bằng (AVL) là cây BST mà
tại mỗi nút của nó độ cao của cây con trái và của cây con phải chênh lệch không quá 1
Binary Search Tree AVL Tree
Trang 51Xây dựng AVL Tree
Để xây dựng được cây AVL thì các bạn nắm rõ các yếu
tố sau:
Trang 52Khái niệm “chiều cao”
(4)
Tính chiều cao qua hàm đệ quy:
int getHeight( Node* root ) {
if ( root == NULL )
return 0 ;
return 1 + max(getHeight( root
-> left ), getHeight( root -> right ));
}
Chiều cao của một Node chính là số
các node trên một đường dẫn dài
nhất từ Node đó đến Node = NULL
Và quy ước Node = NULL có chiều
cao bằng 0
Trang 53Các kỹ thuật quay cây AVL
1 Kỹ thuật quay phải (áp dụng cho cây lệch trái)
2 Kỹ thuật quay trái (áp dụng cho cây lệch phải)
Trang 541 Kỹ thuật quay phải
Cập nhật lại chiều cao:
height (root) = 1 + max (1, 1) = 2 height (X) = 1 + max (root, 2) = 3
(3)
(2)
Trang 55Node* rightRotate( Node* root ){
Node * x = root -> left ;
// Bắ2 t đâI u quay pha (i
root -> left = x -> right ;
x -> right = root ;
// Thiể2 t lập chiểI u cao
root -> height = 1 + max(getHeight( root -> left ),
getHeight( root -> right ));
x -> height = 1 + max(getHeight( x -> left ), getHeight( x
Trang 562 Kỹ thuật quay trái
Y.left = root Cập nhật lại chiều cao:
height (root) = 1 + max (1, 1) = 2 height (Y) = 1 + max (root, 2) = 3
(2)
(3)
Trang 57Node* leftRotate( Node* root ) {
Node * y = root -> right ;
// Bắ2 t đâI u quay trái
root -> right = y -> left ;
y -> left = root ;
// Thiể2 t lập chiểI u cao
root -> height = 1 + max(getHeight( root -> left ),
getHeight( root -> right ));
y -> height = 1 + max(getHeight( y -> left ), getHeight( y
Trang 58Các trường hợp cây bị lệch
1 Trường hợp trái trái – left left
2 Trường hợp trái phải – left right
3 Trường hợp phải phải – right right
4 Trường hợp phải trái – right left
Trang 591 Trường hợp trái trái – left left
Có thể thấy rằng height(X) – height(Y) = 2 => cây bị lệch sang bên trái
value(Vàng) < value(X) => Node vàng nằm bên trái => “Trái trái”.
Xảy ra khi: height(X) – height(Y) > 1 và value(Vàng) < value(X)
Xử lý: quay phải Node root – rotateRight(root)
Trang 602 Trường hợp trái phải – left right
Cây đã bị lệch sang bên trái Node vàng nằm bên phải so với Node X
19
23 AVL Tree
Left left case
Xảy ra khi: height(X) – height(Y) > 1 và value(Vàng) > value(X)
Xử lý: rotateLeft(X) => rotateRight(root)
Trang 613 Trường hợp phải phải – right right
Trang 624 Trường hợp phải trái – right left
Cây đã bị lệch sang bên phải Node vàng nằm bên trái so với Node Y
rotateRight(Y) rotateLeft(root)
Xảy ra khi: height(X) – height(Y) < -1 và value(Vàng) < value(Y)
Xử lý: rotateRight(Y) -> rotateLeft(root)
Trang 63Thank you
for watching!