Nhập môn CTDL và thuật toán 9 Một số khái niệm cơ bản • Bậc của một nút : là số cây con của nút đó • Bậc của một cây : là bậc lớn nhất của các nút trong cây.. Nhập môn CTDL và thuật to
Trang 1Cấu trúc cây (Tree)
Trang 2Nhập môn CTDL và thuật toán
2
Nội dung
• Giới thiệu khái niệm cấu trúc cây.
• Cấu trúc dữ liệu cây nhị phân, cây nhị phân tìm kiếm
• Giới thiệu cấu trúc dữ liệu cây nhị phân tìm kiếm cân bằng.
Trang 3Mỗi nút ở cấp i sẽ quản lý một số nút ở cấp i+1 Quan hệ này người ta còn gọi là quan hệ cha-con
Trang 4• Hoặc là tập rỗng (cây rỗng)
• Hoặc tập khác rỗng trong đó có một nút gốc , các nút còn lại được phân thành
nhóm, trong đó mỗi nhóm là một cây (cây con)
Trang 5Nội địa Quốc tế
Trang 7Nhập môn CTDL và thuật toán
7
Trang 8Nhập môn CTDL và thuật toán
8
Trang 9Nhập môn CTDL và thuật toán
9
Một số khái niệm cơ bản
• Bậc của một nút : là số cây con của nút đó
• Bậc của một cây : là bậc lớn nhất của các nút trong cây Cây có bậc n thì gọi là cây n- phân
• Nút gốc : là nút không có nút cha
• Nút lá : là nút có bậc bằng 0
• Nút trong : là nút có bậc khác 0 và không
Trang 10Nhập môn CTDL và thuật toán
10
Owner Jake
Manager Chef Brad Carol
Waitress Waiter Cook Helper Joyce Chris Max Len
Ví dụ
ROOT NODE
Trang 11Nhập môn CTDL và thuật toán
11
Owner Jake
Manager Chef Brad Carol
Waitress Waiter Cook Helper Joyce Chris Max Len
Ví dụ : Nút Lá
NODE lá
Trang 15Nhập môn CTDL và thuật toán
15
Owner Jake
Manager Chef Brad Carol
Waitress Waiter Cook Helper Joyce Chris Max Len
A Tree Has Levels
LEVEL 0
Trang 16Nhập môn CTDL và thuật toán
16
Owner Jake
Manager Chef Brad Carol
Waitress Waiter Cook Helper Joyce Chris Max Len
Level One
LEVEL 1
Trang 17Nhập môn CTDL và thuật toán
17
Owner Jake
Manager Chef Brad Carol
Waitress Waiter Cook Helper Joyce Chris Max Len
Level Two
LEVEL 2
Trang 19Nhập môn CTDL và thuật toán
19
Một số khái niệm cơ bản
• Độ dài đường đi từ gốc đến nút x:
Px = số nhánh cần đi qua kể từ gốc đến x
• Độ dài đường đi tổng của cây :
trong đó Px là độ dài đường đi từ gốc đến X
• Độ dài đường đi trung bình : PI = PT/n (n là số nút trên cây T)
• Rừng cây: là tập hợp nhiều cây trong đó thứ tự các cây là quan trọng
Trang 20Nhập môn CTDL và thuật toán
20
Nhận xét
• Trong cấu trúc cây không tồn tại chu trình
• Tổ chức của cấu trúc cây cho phép truy cập nhanh đến các phần tử của nó
Trang 21Nhập môn CTDL và thuật toán
21
Depth-first Search
Trang 22Nhập môn CTDL và thuật toán
22
Breadth-first Search
Trang 24Cây con phải
HÌnh ảnh một cây nhị phân
Trang 27Nhập môn CTDL và thuật toán
27
Owner Jake
Manager Chef Brad Carol
Waitress Waiter Cook Helper Joyce Chris Max Len
A Subtree
LEFT SUBTREE OF ROOT NODE
Trang 28Nhập môn CTDL và thuật toán
28
Owner Jake
Manager Chef Brad Carol
Waitress Waiter Cook Helper Joyce Chris Max Len
Another Subtree
RIGHT SUBTREE
OF ROOT NODE
Trang 29• Số nút lá ≤ 2h-1, với h là chiều cao của cây
• Chiều cao của cây h ≥ log2(số nút trong cây)
• Số nút trong cây ≤ 2h-1
Trang 304 5 6 8
0 1 2 3 4 5 6 7 8
…
2k+1, 2k+2 k=3
Trang 31– Thông tin lưu trữ tại nút.
– Địa chỉ nút gốc của cây con trái trong bộ nhớ – Địa chỉ nút gốc của cây con phải trong bộ nhớ
Trang 32Nhập mơn CTDL và thuật tốn
32
Biểu diễn cây nhị phân T
typedef struct TNODE
{
Data Key;
// Data là kiểu dữ liệu ứng với thông tin lưu tại nút
TNODE *pLeft, *pRight;
};
typedef TNODE * TREE ; TREE root;
Trang 34Nhập môn CTDL và thuật toán
34
Duyệt cây nhị phân
• Có 3 kiểu duyệt chính có thể áp dụng trên cây nhị phân:
– Duyệt theo thứ tự trước (NLR) – Duyệt theo thứ tự giữa (LNR) – Duyệt theo thứ tự sau (LRN)
• Tên của 3 kiểu duyệt này được đặt dựa trên trình tự của việc thăm nút gốc so với việc thăm 2 cây con
Trang 35Nhập mơn CTDL và thuật tốn
35
Duyệt theo thứ tự trước (Node-Left-Right)
của cây con trái rồi đến cây con phải Thủ tục duyệt cĩ thể trình bày đơn giản như sau:
void NLR( TREE Root) {
if (Root != NULL) {
<Xử lý Root> ; //Xử lý tương ứng theo nhu cầu
NLR(Root->pLeft);
NLR(Root->pRight);
} }
Trang 36L P
G M
A
Trang 37Nhập mơn CTDL và thuật tốn
37
Duyệt theo thứ tự giữa (Left- Node-Right)
• Kiểu duyệt này trước tiên thăm các nút của cây con trái sau đĩ thăm nút gốc rồi đến cây con phải
• Thủ tục duyệt cĩ thể trình bày đơn giản như sau:
void LNR ( TREE Root) {
if (Root != NULL ) {
LNR (Root->pLeft);
<Xử lý Root> ; //Xử lý tương ứng theo nhu cầu
LNR (Root->pRight);
} }
Trang 38L P
G M
H
Trang 39Nhập mơn CTDL và thuật tốn
39
Duyệt theo thứ tự sau (Left-Right-Node)
• Kiểu duyệt này trước tiên thăm các nút của cây con trái sau
đĩ thăm đến cây con phải rồi cuối cùng mới thăm nút gốc
• Thủ tục duyệt cĩ thể trình bày đơn giản như sau:
void LRN ( TREE Root) {
if (Root != NULL ) {
LRN (Root->pLeft);
LRN (Root->pRight);
<Xử lý Root> ; //Xử lý tương ứng theo nhu cầu
} }
Trang 40L P
G M
H
Trang 41về ứng dụng của duyệt
theo thứ tự sau là việc xác định tồng kích thước của một thư mục trên đĩa
Trang 42Nhập môn CTDL và thuật toán
42
Duyệt theo thứ tự sau (Left-Right-Node)
• Tính toán giá trị của biểu thức dựa trên cây biểu thức
(3 + 1) × 3/(9 – 5 + 2) – (3 × (7 – 4) + 6) = –13
Trang 43Nhập môn CTDL và thuật toán
43
Một cách biểu diễn cây nhị phân khác
• Đôi khi, khi định nghĩa cây nhị phân, người ta quan tâm đến cả quan hệ 2 chiều cha con chứ không chỉ một chiều như định nghĩa ở phần trên
• Lúc đó, cấu trúc cây nhị phân có thể định nghĩa lại như sau:
typedef struct tagTNode
{
DataType Key;
struct tagTNode * pParent;
struct tagTNode * pLeft;
struct tagTNode * pRight;
} TNODE ;
typedef TNODE * TREE ;
Trang 46Nhập môn CTDL và thuật toán
46
Đếm số node
Trang 48Nhập môn CTDL và thuật toán
48
Đếm số node lá
Trang 50Nhập môn CTDL và thuật toán
50
Tính chiều cao
Trang 53cả các nút thuộc cây con phải
• Nếu số nút trên cây là N thì chi phí tìm kiếm trung bình chỉ khoảng log2N
Trang 55Nhập môn CTDL và thuật toán
55
Trang 56Nhập môn CTDL và thuật toán
56
Trang 61Nhập môn CTDL và thuật toán
61
Duyệt cây nhị phân tìm kiếm
• Thao tác duyệt cây trên cây nhị phân tìm kiếm hoàn toàn giống như trên cây nhị phân
Trang 6235 32
50 41
Duyệt inorder: 1 3 5 6 10 12 13 18 20 25 29 32 35 37 41 50
Trang 6335 32
50 41
Duyệt postorder:
Trang 6435 32
50 41
Duyệt preorder:
Trang 6535 32
50 41
Tìm kiếm 13
Khác nhau Giống nhau Node gốc nhỏ hơn Node gốc lớn hơn
Tìm thấy Số node duyệt: 5 Số lần so sánh: 9
Trang 6635 32
50 41
Tìm kiếm 14
Khác nhau Node gốc nhỏ hơn Node gốc lớn hơn
Không tìm thấy Số node duyệt: 5 Số lần so sánh: 10
Trang 67if (T->Key == X)
return T;
if (T->Key > X)
return searchNode (T->pLeft, X);
return searchNode (T->pRight, X);
}
return NULL;
}
Trang 69• Như vậy thao tác tìm kiếm trên CNPTK có
n nút tốn chi phí trung bình khoảng O(log2n)
Trang 70Nhập môn CTDL và thuật toán
70
Thêm một phần tử x vào cây
• Việc thêm một phần tử X vào cây phải bảo đảm
• Ta có thể thêm vào nhiều chỗ khác nhau trên cây, nhưng nếu thêm vào một nút ngoài sẽ là tiện lợi nhất do ta có thể thực hiện quá trình tương tự thao tác tìm kiếm
• Khi chấm dứt quá trình tìm kiếm cũng chính là lúc tìm được chỗ cần thêm
Trang 72Nhập mơn CTDL và thuật tốn
72
Thêm một phần tử x vào cây
int insertNode ( TREE &T, Data X) { if (T) {
if (T->Key == X) return 0; // đã có
if (T->Key > X)
return insertNode (T->pLeft, X);
else return insertNode (T->pRight, X);
}
T = new TNode ;
if (T == NULL) return -1; // thiếu bộ nhớ
T->Key = X;
T->pLeft = T->pRight = NULL;
return 1; // thêm vào thành công
}
Trang 733
Trang 7430, 12, 17, 49, 22, 65, 51, 50, 70, 68
Trang 77Nhập mơn CTDL và thuật tốn
77
Trường hợp 2: X chỉ cĩ 1 con (trái hoặc phải)
• trước khi hủy X ta móc nối cha của X với con duy nhất của nó
Trang 78Nhập môn CTDL và thuật toán
78
Trường hợp 3: X có đủ 2 con
• Không thể hủy trực tiếp do X có đủ 2 con
• Hủy gián tiếp:
Phần tử này có tối đa một con
hợp đầu
• Vấn đề: chọn Y sao cho khi lưu Y vào vị trí của
X, cây vẫn là CNPTK
Trang 79• Có 2 phần tử thỏa mãn yêu cầu:
• Việc chọn lựa phần tử nào là phần tử thế mạng hoàn toàn phụ thuộc vào ý thích của người lập trình
• Ở đây, ta sẽ chọn phần tử trái nhất trên cây con phải làm phân tử thế mạng.
Trang 8030 23
Trang 81void searchStandFor (TREE &p, TREE &q)
Trang 82if (T->Key > X) return delNode (T->pLeft, X);
if (T->Key < X) return delNode (T->pRight, X);
Trang 90Nhập môn CTDL và thuật toán
90
Ví dụ
Trang 93Nhập môn CTDL và thuật toán
93
Ví dụ
Trang 94Nhập môn CTDL và thuật toán
94
Ví dụ
Trang 96Nhập môn CTDL và thuật toán
96
Hủy toàn bộ cây nhị phân tìm kiếm
• Việc toàn bộ cây có thể được thực hiện thông qua thao tác duyệt cây theo thứ tự sau Nghĩa là ta sẽ hủy cây con trái, cây con phải rồi mới hủy nút gốc
void removeTree ( TREE &T) {
Trang 97Nhập môn CTDL và thuật toán
97
Nhận xét
đều có độ phức tạp trung bình O(h), với h là chiều cao của cây
độ cao h = log2(n) Chi phí tìm kiếm khi đó sẽ tương đương tìm kiếm nhị phân trên mảng có thứ tự
1 danh sách liên kết (khi mà mỗi nút đều chỉ có 1 con trừ nút lá) Lúc đó các thao tác trên sẽ có độ phức tạp O(n)
chi phí cho các thao tác là log2(n)
Trang 102Nhập môn CTDL và thuật toán
102
Cây AVL
• Lịch sử cây cân bằng (AVL Tree):
– AVL là tên viết tắt của các tác giả người Nga
đã đưa ra định nghĩa của cây cân bằng Adelson-Velskii và Landis (1962)
– Từ cây AVL, người ta đã phát triển thêm nhiều loại CTDL hữu dụng khác như cây đỏ-đen
(Red-Black Tree), B-Tree, …
• Cây AVL có chiều cao O(log2(n))
Trang 105Nhập mơn CTDL và thuật tốn
105
Định nghĩa
#define LH -1 /* Cây con trái cao hơn */
#define EH 0 /* Hai cây con bằng nhau */
#define RH 1 /* Cây con phải cao hơn */
typedef struct tagAVLNode {
char balFactor; // Chỉ số cân bằng
Data key;
struct tagAVLNode * pLeft;
struct tagAVLNode * pRight;
} AVLNode ;
typedef AVLNode * AVLTree ;
Trang 106• Việc cân bằng lại một cây sẽ phải thực hiện sao cho chỉ ảnh hưởng tối thiểu đến cây nhằm giảm thiểu chi phí cân bằng
• Các thao tác đặc trưng của cây AVL:
– Thêm một phần tử vào cây AVL.
– Hủy một phần tử trên cây AVL.
– Cân bằng lại một cây vừa bị mất cân bằng
Trang 107• Như vậy, khi mất cân bằng, độ lệch chiều cao giữa 2 cây con sẽ là 2
• Có 6 khả năng sau:
Trang 108h-1 h-1
Trang 109R L
h-1
L h-1
Trang 110• Vì vậy, chỉ cần khảo sát trường hợp lệch
về bên trái
• Trong 3 trường hợp lệch về bên trái, trường hợp T1 lệch phải là phức tạp nhất Các trường hợp còn lại giải quyết rất đơn giản
Trang 111Nhập môn CTDL và thuật toán
111
Cân bằng lại cây AVL
• T/h 1.1: cây T1 lệch về bên trái Ta thực hiện phép quay đơn Left-Left
L
R
T1
T L1
R1 h
Trang 112R h-1
Trang 113từ trạng thái mất cân bằng do lệch trái thành mất cân bằng do lệch phải ? cần áp dụng cách khác
Trang 114L2 R2
T T1
L1
h-1
R h-1 T2
Trang 115– Sau khi cân bằng,
trường hợp duy nhất sau khi cân bằng nút T cũ có chỉ số cân bằng ≠ 0
đều có độ phức tạp O(1).
Trang 116Nhập môn CTDL và thuật toán
116
Cân bằng lại cây AVL
• T/h 1.1: cây T1 lệch về bên trái Ta thực hiện phép quay đơn Left-Left
L
R
T1
T L1
R1 h
Trang 117Nhập môn CTDL và thuật toán
117
Quay đơn Left-Left
void rotateLL ( AVLTree &T) //quay đơn Left-Left
break ;
case EH : T->balFactor = LH ; T1->balFactor = RH ;
break ; }
T = T1;
}
Trang 118Nhập môn CTDL và thuật toán
118
Quay đơn Right-Right
void rotateRR( AVLTree &T)//quay đơn Right-Right
break ;
case EH : T->balFactor = RH ; T1->balFactor= LH ;
break ; }
T = T1;
}
Trang 119Nhập môn CTDL và thuật toán
119
Quay keùp Left-Right
void rotateLR ( AVLTree &T)//quay kép Left-Right
T = T2;
}
Trang 120Nhập môn CTDL và thuật toán
120
Quay keùp Right-Left
void rotateRL ( AVLTree &T)//quay kép Right-Left
T = T2;
}
Trang 121Nhập môn CTDL và thuật toán
121
Cân bằng khi cây bị lêch về bên trái
int balanceLeft ( AVLTree &T)
//Cân bằng khi cây bị lêch về bên trái
{
AVLNode * T1 = T->pLeft;
switch (T1->balFactor) {
case LH : rotateLL (T); return 2;
case EH : rotateLL (T); return 1;
case RH : rotateLR (T); return 2;
}
return 0;
}
Trang 122Nhập môn CTDL và thuật toán
122
Cân bằng khi cây bị lêch về bên phải
int balanceRight ( AVLTree &T )
//Cân bằng khi cây bị lêch về bên phải
{
AVLNode * T1 = T->pRight;
switch (T1->balFactor) {
case LH : rotateRL (T); return 2;
case EH : rotateRR (T); return 1;
case RH : rotateRR (T); return 2;
}
return 0;
}
Trang 123Nhập môn CTDL và thuật toán
123
Thêm một phần tử trên cây AVL
như trên CNPTK
trí thêm vào, ta phải lần ngược lên gốc để kiểm tra xem
có nút nào bị mất cân bằng không Nếu có, ta phải cân bằng lại ở nút này
bằng
nhớ, gặp nút cũ hay thành công Nếu sau khi thêm, chiều cao cây bị tăng, giá trị 2 sẽ được trả về
Trang 124Nhập môn CTDL và thuật toán
124
Thêm một phần tử trên cây AVL
int insertNode ( AVLTree &T, DataType X) { int res;
if (T) { if (T->key == X) return 0; //đã có
if (T->key > X) { res = insertNode (T->pLeft, X);
if (res < 2) return res;
switch (T->balFactor) { case RH : T->balFactor = EH ; return 1;
case EH : T->balFactor = LH ; return 2;
case LH : balanceLeft (T); return 1;
} } .
}
insertNode2
Trang 125Nhập môn CTDL và thuật toán
125
Thêm một phần tử trên cây AVL
int insertNode ( AVLTree &T, DataType X) {
else // T->key < X
{ res = insertNode (T-> pRight, X);
if (res < 2) return res;
switch (T->balFactor) { case LH : T->balFactor = EH ; return 1;
case EH : T->balFactor = RH ; return 2;
case RH : balanceRight (T); return 1;
} }
}
insertNode3
Trang 126Nhập môn CTDL và thuật toán
126
Thêm một phần tử trên cây AVL
int insertNode ( AVLTree &T, DataType X) {
T = new TNode;
if (T == NULL ) return -1; //thiếu bộ nhớ
T->key = X;
T->balFactor = EH ; T->pLeft = T->pRight = NULL ;
return 2; // thành công, chiều cao tăng
}
Trang 127Nhập môn CTDL và thuật toán
127
Hủy một phần tử trên cây AVL
phần tử X ra khỏi cây AVL thực hiện giống như trên CNPTK
thực hiện việc cân bằng lại
tạp hơn nhiều do có thể xảy ra phản ứng dây chuyền
không có X trong cây Nếu sau khi h?y, chiều cao cây bị giảm, giá trị 2 sẽ được trả về:
Trang 128Nhập môn CTDL và thuật toán
128
Hủy một phần tử trên cây AVL
{ int res;
if (T== NULL ) return 0;
if (T->key > X) { res = delNode (T->pLeft, X);
if (res < 2) return res;
switch (T->balFactor) { case LH : T->balFactor = EH ; return 2;
case EH : T->balFactor = RH ; return 1;
case RH : return balanceRight (T);
} } // if(T->key > X)
}
delNode2
Trang 129Nhập môn CTDL và thuật toán
129
Hủy một phần tử trên cây AVL
{
if (T->key < X) { res = delNode (T->pRight, X);
if (res < 2) return res;
switch (T->balFactor)
{ case RH : T->balFactor = EH ; return 2;
case EH : T->balFactor = LH ; return 1;
case LH : return balanceLeft (T);
} } // if(T->key < X)
}
delNode3
Trang 130Nhập môn CTDL và thuật toán
130
Hủy một phần tử trên cây AVL
{
{ AVLNode * p = T;
if (T->pLeft == NULL ) { T = T->pRight; res = 2; }
else if (T->pRight == NULL ) { T = T->pLeft; res = 2; }
else //T có đủ cả 2 con
{ res = searchStandFor (p,T->pRight);
if (res < 2) return res;
switch (T->balFactor)
{ case RH : T->balFactor = EH ; return 2;
case EH : T->balFactor = LH ; return 1;
case LH : return balanceLeft (T);
}
}
delete p; return res;
} }