Tìm hiểu BTree (B Cây), cấu trúc dữ liệu dùng để lưu dữ liệu lớn, truy xuất nhanh, hiệu quả, độ phức tạp thuật toán tốt. Được ứng dụng để lưu các tập tin lớn trong hệ điều hành, các định dạng cơ sở dữ liệu của oracle, sql, ...
Trang 1Mục Lục:
Mục Lục: 1
I Mở đầu 2
II Mô tả bài toán 3
1 Giới thiệu B-Tree 3
1.1 Các dạng cây thông thường 3
Cây 3
Cây nhị phân 4
Cây nhị phân tìm kiếm 4
Cây AVL 5
Cây tìm kiếm M-Phân 5
1.2 Định nghĩa B-Tree 6
2 Các thao tác và cài đặt B-Tree 7
2.1 Tìm kiếm một khóa trong B-Tree 7
2.2 Thêm khóa vào B-Tree 8
2.3 Xóa một khóa khỏi B-Tree 11
2.4 Cài đặt 13
3 Đánh giá và so sánh 25
3.1 Đánh giá độ phức tạp 25
3.2 So sánh với các cây khác 26
4 Ứng dụng B-Tree 27
5 Tham khảo 27
Trang 2I Mở đầu
Thế giới ngày càng phát triển với sự ứng dụng mạnh mẽ công nghệ thông tin(CNTT) vào mọi mặt đời sống Từ chăm sóc sức khỏe con người, mua sắm, tra cứu thông tin, cho đến các lĩnh vực kinh doanh, sản xuất, … Cũng vì thế mà các ứng dụng CNTT thu thập được một lượng lớn dữ liệu, nhưng dữ liệu về thông tin người dùng, những dữ liệu về những đối tượng khác như là: thời tiết, chứng khoán, giao thông, … Những dữ liệu đó được gọi với thuật ngữ “dữ liệu lớn”, hay là “Big Data”.
Con người luôn muốn mọi thứ “thông minh hơn”, nên những nguồn
dữ liệu như vậy rất quý giá, nó sẽ được phân tích và “khai thác” Nhưng nó quá lớn, vậy nên trước khi tính đến lợi ích từ việc “khai thác”, nó phải được
tổ chức, lưu trữ làm sao để có thể khai thác một cách nhanh nhất, dễ dàng nhất nhưng phải đảm bảo phù hợp với nguồn tài nguyên của các thiết bị
xử lý hiện nay, điển hình là máy tính
Có nhiều cách để tổ chức lưu trữ 1 lượng dữ liệu lớn, đơn giản và phổ biến như là: Hash, Heap, Index và B-Tree Và B-Tree là cấu trúc được lựa chọn để trình bày trong đồ án này.
Trang 3II Mô tả bài toán
1.Giới thiệu B-Tree
Trước khi đi vào B-Tree cần phải biết cấu trúc của một số loại cây đơn giản khác trước.
Cây
Cây là một tập hợp T các phần tử (gọi là nút (node) của cây) trong
đó có 1 nút đặc biệt được gọi là gốc, các nút còn lại được chia thành những tập rời nhau T1, T2 , , Tn theo quan hệ phân cấp trong đó Ti cũng là một cây.
Cấu trúc cây không chứa chu trình.
Khái niệm một số thành phần của cây:
o Bậc của một nút: Là số cây con của nút đó.
o 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à n-phân Ví dụ: Nhị phân, tam phân…
o Nút gốc (Root Node): Là nút không có nút cha.
o Nút lá (Leaf Node): Là nút có bậc bằng 0
o Nút nhánh: Là nút có bậc khác 0 và không phải nút gốc
o Mức của một nút: Nút gốc có mức =0, các nút có nút được tăng lên 1.
Hình 1.1 Một cây đơn giản tam phân
Trang 4Hình 1.2 Một đồ thì không phải là cây vì chứa chu trình
Cây nhị phân
Là cây mà bậc của mỗi nút tối đa là 2, tức là có tối đa 2 cây con.
Hình 1.3: Một cây nhị phân
Cây nhị phân tìm kiếm
Cây nhị phân tìm kiếm (CNPTK) là cây mà tại mỗi nút, giá trị của nút
đó luôn lớn hơn (hoặc bằng) giá trị của nút con bên trái và nhỏ hơn (hoặc bằng) giá trị nút con bên phải.
Nhờ cấu trúc như vậy nên tìm kiếm trên cây có đinh hướng, và
phương pháp tìm kiếm nhị phân thường được áp dùng nhất.
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, xấu nhất n.
Trang 5Hình 1.4: Một cây tìm kiếm nhị phân.
B : Cây AVL, tất cả các node đều có HSCB từ -1 đến 1
Cây tìm kiếm M-Phân
Ở mỗi nút có tối đa M nút con.
Mỗi nút có M-1 Khóa Vậy nên M càng lớn thì chiều cao cây càng thấp.
Trang 6Hình 1.6: Cây M-Phân.
Cấu trúc cây là một trong những cấu trúc tốt nhất dùng để biểu diễn đồ thị và lưu trữ dữ liệu Vì nó đáp ứng được nhiều yêu cầu khó mà nhiều cấu trúc khác khác không đáp ứng được như:
Lưu trữ số phần tử rất lớn
Lưu trữ trên bộ nhớ ngoài
Tìm kiếm nhanh
Cấu trúc cây mà ở đây là B-Tree đáp ứng được các yêu cầu đó:
B-Tree được giới thiệu bởi R Bayer và E McGreight vào năm 1972 Tên B-Tree cũng được lấy theo tên của họ.
Được phát triển từ ý tưởng của cây 2-3 (2-3 Tree), mỗi nút có nhiều hơn 1 khóa, và kết hợp với cây tìm kiếm.
B-Tree là một cây tìm kiếm M-Phân, nên có chiều cao thấp, dữ liệu được gom thành những block nên giảm số lần truy xuất.
Một cây được gọi là B-Tree bậc m (m>2) thì đó phải đáp ứng được
những điều kiện:
Nút gốc (nếu không phải nút lá) thì có từ 2 tới tối đa m cây con.
Ở mỗi nút, ngoại trừ nút gốc và nút lá, thì có từ m/2 đến tối đa m cây con Và có từ (m/2)-1 tới tối đa m-1 khóa.
Cây đã cân bằng, mọi nút là đều nằm cùng một mức.
Các khóa và cây con được sắp xếp theo cây tìm kiếm.
Trang 7Ví dụ: B-Tree bậc 4 và không phải B-Tree bậc 4 ở hình 1.7 và hình 1.8.
Hình 1.7: B-Tree bậc 4
Hình 1.8: Không phải là B-Tree bậc 4 do cây con thứ 3 của nút gốc cho
có 1 con, trong khi mỗi nút của B-Tree bậc 4 có ít nhất là 2 con
2.Các thao tác và cài đặt B-Tree
Do các khóa các nút trong B-Tree được sắp xếp theo tìm kiếm nhị phân Nên sử dụng tìm kiếm nhị phân trong B-Tree.
Ví dụ minh họa:
Trang 8Hình 2.1: B-Tree ban đầu
Tìm khóa 45:
Hình 2.2: Bắt đầu từ nút gốc
Hình 2.3: Do 42<45, nên đi tiếp tới cây con thứ 2
Hình 2.4: 45<59 nên sẽ duyệt tiếp xuống cây con thứ nhất
Trang 9Hình 2.5: 45<50 nên duyệt xuống cây con thứ nhất, và đây cũng là vị
trí khóa cần tìm
Để xây dựng một B-Tree thì phải thêm từng khóa một vào cây.
Ý tưởng: Tìm vị trí khóa có thể thêm vào cây Việc tìm kiếm sẽ kết thúc
tại một lá Khóa mới sẽ được thêm vào nút lá:
Nếu chưa đầy: việc thêm vào hoàn tất.
Nếu đầy: Phân đôi nút lá cần thêm:
o Tách nút lá ra làm hai nút cạnh nhau trong cùng một mức.
o Chuyển phần tử giữa lên nút cha.
o Quá trình phân đôi các nút có thể được lan truyền ngược về gốc và kết thúc khi có một nút cha nào đó cần được thêm một khóa gởi từ dưới lên mà chưa đầy.
Ví dụ bằng hình ảnh: Thêm một khóa có giá trị =38 vào một B-Tree bậc
3:
Hình 2.6: B-Tree ban đầu
Trang 10Hình 2.7: Bắt đầu ở nút gốc.
Hình 2.8: Do 17<38<42 nên duyệt vào cây con thứ 2
Hình 2.9: 36<38 nên duyệt vào cây con thứ 3, đây cũng là nút lá
Hình 2.10: Thêm khóa vào nút ở lá Và số khóa trong nút vượt quá giới
hạn(B-Tree bậc 3 nên số khóa giới hạn là 2)
Hình 2.11: Tách ra làm 2 nút, đưa khóa ở giữa lên nút cha
Trang 11Hình 2.12: Số khóa ở nút cha mới được thêm vào cũng vượt quá giới hạn
Hình 2.13: Tách ra làm 2 nút và đưa khóa ở giữa lên nút cha Và số
khóa ở nút cha (nút gốc) cũng vượt quá giới hạn
Hình 2.14: Nên sẽ lấy khóa ở giữa đưa lên làm nút gốc mới Và cây hoàn
tất thêm khóa 38
Nếu khóa này thuộc nút lá, đơn giản xóa khóa này khỏi nút lá đó.
Nếu khóa này thuộc một nút trong:
o Tìm khóa lớn nhất thuộc cây con trái (phải nhất) hoặc khóa nhỏ nhất thuộc cây con phải (trái nhất) đưa lên thay thế cho khóa cần xóa.
o Xóa khóa trái nhất hoặc phải nhất ở cây con tương ứng.
Việc xóa 1 khóa khỏi một nút có thể đòi hỏi phải cân bằng lại cây:
o Nếu một trong các nút anh em kế cận nút đang xét có số lượng khóa nhiều hơn số lượng tối thiểu, đưa một khóa của nút anh em lên nút cha và đưa một khóa ở nút cha xuống nút đang xét
o Nếu tất cả các nút anh em kế cận nút đang xét đều có số lượng khóa vừa đủ số lượng tối thiểu, chọn một nút anh em kế cận và hợp
Trang 12nhất nút anh em này với nút đang xét (và khóa tương ứng ở nút cha) Nếu nút cha trở nên thiếu khóa, lặp lại quá trình này.
Ví dụ:
Hình 2.15: B-Tree bậc 3
Xóa một khóa ở nút lá:
Hình 2.16: Xóa khóa 15 ở nút lá
Hình 2.17: B-Tree sau khi xóa một khóa ở nút lá
Xóa một khóa ở nút trong:
Xóa khóa 18:
Hình 2.18: B-Tree ban đầu
Trang 13Hình 2.19: Tìm vị trí khóa 18.
Hình 2.20: Xóa khóa 18 sẽ làm số khóa của nút dưới mức tối thiểu,
nên đưa khóa 19 từ nút con lá lên
Hình 2.21: B-Tree sau khi xóa khóa 18
Xóa khóa 19:
Hình 2.22: Tìm vị trí khóa 19
Hình 2.23: Nếu xóa khóa 19 xẽ làm số khóa của nút dưới mức tối thiểu,
Nên đưa khóa 26 từ nút lá lên
Trang 14Hình 2.24: Xóa khóa 19, nút sẽ có 2 khóa và 2 con, không hợp lệ,
nên kéo một khóa xuống nút con
int val[MAX+1], count;
struct btreeNode *link[MAX + 1];
};
struct btreeNode *root;
/* creating new node */
struct btreeNode * createNode(int val, struct btreeNode *child) {
struct btreeNode *newNode;
newNode = (struct btreeNode *)malloc(sizeof(struct btreeNode));
Trang 15}
/* Places the value in appropriate position */
void addValToNode(int val, int pos, struct btreeNode *node,
struct btreeNode *child) {
/* split the node */
void splitNode (int val, int *pval, int pos, struct btreeNode *node,
struct btreeNode *child, struct btreeNode **newNode) {
(*newNode)->val[j - median] = node->val[j];
(*newNode)->link[j - median] = node->link[j];
Trang 16/* sets the value val in the node */
int setValueInNode(int val, int *pval,
struct btreeNode *node, struct btreeNode **child) {
Trang 17if (val == node->val[pos]) {// duplicate
printf("Duplicates not allowed\n");
/* insert val in B-Tree */
void insertion(int val) {
int flag, i;
struct btreeNode *child;
flag = setValueInNode(val, &i, root, &child);
if (flag)
root = createNode(i, child);
}
/* copy successor for the value to be deleted */
void copySuccessor(struct btreeNode *myNode, int pos) {
struct btreeNode *dummy;
dummy = myNode->link[pos];
Trang 18for (;dummy->link[0] != NULL;)
dummy = dummy->link[0];
myNode->val[pos] = dummy->val[1];
}
/* removes the value from the given node and rearrange values */
void removeVal(struct btreeNode *myNode, int pos) {
/* shifts value from parent to right child */
void doRightShift(struct btreeNode *myNode, int pos) {
struct btreeNode *x = myNode->link[pos];
Trang 19/* shifts value from parent to left child */
void doLeftShift(struct btreeNode *myNode, int pos) {
Trang 20struct btreeNode *x1 = myNode->link[pos], *x2 = myNode->link[pos - 1];
/* adjusts the given node */
void adjustNode(struct btreeNode *myNode, int pos) {
Trang 21if(myNode->link[pos - 1]->count > MIN) {
/* delete val from the node */
int delValFromNode(int val, struct btreeNode *myNode) {
int pos, flag = 0;
for (pos = myNode->count;
(val < myNode->val[pos] && pos > 1); pos );
if (val == myNode->val[pos]) {
flag = 1;
} else {
flag = 0;
Trang 22/* delete val from B-tree */
void deletion(int val, struct btreeNode *myNode) {
Trang 23/* search val in B-Tree */
void searching(int val, int *pos, struct btreeNode *myNode) {
Trang 24printf("1 Insertion\t2 Deletion\n");
printf("3 Searching\t4 Traversal\n");
printf("5 Exit\nEnter your choice:");
scanf("%d", &ch);
switch (ch) {
case 1:
Trang 25printf("Enter your input:");
Trang 263.Đánh giá và so sánh
Lưu trữ một lượng lớn dữ liệu trên đĩa cứng, mà tốc độ truy xuất của đĩa cứng nhỏ hơn rất nhiều bộ nhớ trong của máy tính (RAM), nên số lượng nút cần duyệt để tìm ra một khóa cũng là điều đáng lưu tâm Trường hợp xấu nhất là duyệt từ nút gốc đến tận nút lá Ở B-Tree do các nút lá đều có
cùng một mức nên số nút cần duyệt qua cũng chính là chiều cao h của
cây.
Để ước lượng được chiều cao của cây thì cần tính được số khóa của
cây, từ các điều kiện của B-Tree Một B-Tree bậc m thì:
Nút gốc có ít nhất 1 khóa.
Ở mức một, có ít nhất 2 nút và mỗi nút có ít nhất (m/2)-1, có tổng cộng ít nhất 2[(m/2)-1] khóa.
Mức hai có ít nhất 2(m/2) nút và có ít nhất (m/2)-1 khóa ở mỗi nút, nên có tổng cộng ít nhất 2(m/2)*[(m/2)-1] khóa ở mức 2.
=>Tổng quát, ở mức i, với 1≤ i ≤ h − 1, sẽ có ít nhất 2(m/2)i-1*[(m/2)-1]
khóa.
Ở mức h, tức là ở hàng nút lá, có ít nhất 2(m/2)h-1 nút và mỗi nút có ít nhất một khóa.
Vậy với mọi B-Tree bậc m có n nút và chiều cao h>0 thì có bất phương
Tính toán và ước lượng trên một file có 100 triệu khóa:
Chặn trên của
Trang 27Kết luận: Có thể thấy dù lượng dữ liệu lớn, nhưng chiều cao của cây thấp,
số nút phải duyệt ít, và tìm kiếm theo kiểu tìm kiếm nhị phân Nên độ
phức tạp tìm kiếm chỉ là O(logn) Nhưng giai đoạn tiền xử lý tìm kiếm là
giai đoạn đòi hỏi rất nhiều thời gian, cây cần phải cân bằng đủ các điều kiện thành B-Tree Và thời gian truy xuất trên đĩa cứng lâu, cũng là một điều cần lưu ý.
Không Gian Tìm Khóa Thêm Khóa Xóa Khóa
TrungBình NhấtXấu TrungBình NhấtXấu TrungBình NhấtXấu TrungBình NhấtXấu
BST O(n) O(n) O(logn) O(n) O(logn) O(n) O(logn) O(n)
AVL Tree O(n) O(n) O(logn) O(logn) O(logn) O(logn) O(logn) O(logn)
2-3 Tree O(n) O(n) O(logn) O(logn) O(logn) O(logn) O(logn) O(logn)
B-Tree O(n) O(n) O(logn) O(logn) O(logn) O(logn) O(logn) O(logn)
Bảng 3.1: So sánh độ phức tạp tìm kiếm, thêm khóa, xóa khóa với các
cây khác
Do đều tìm kiếm theo phương thức tìm kiếm nhị phân và có khả năng tự cân bằng cây nên độ phức tạp tìm kiếm của B-Tree không hơn 2-3 Tree, hay là AVL Tree.
Điểm mạnh của B-Tree là mỗi nút có một block các khóa nên chiều cao cây xây dựng ra thấp, số nút cần duyệt để kiếm một khóa ít, phù hợp với duyệt dữ liệu lớn và lưu trên bộ nhớ ngoài.
Thử nghiệm thực tế với cây AVL và B-Tree:
Mỗi lần với n phần tử và khóa tìm kiếm là n/2:
Cây AVL sử dụng bộ nhớ trong để lưu trữ(RAM), B-Tree sử dụng bộ nhớ ngoài(HDD) để lưu trữ cây
Trang 28 Các thao tác thêm và tìm kiếm bằng cây AVL nhanh hơn B-Tree, nhưng không đáng kể, đơn vị tính ở đây là Mini giây và Nano giây
Cả hai đều dùng tìm kiếm nhị phân để thực hiện, nên tốc độ chênh lệch ở đây là do AVL dùng RAM, còn B-Tree sử dụng HDD để lưu trữ.
Khi thử nghiệm với 30 triệu phần tử, thì cây AVL thực hiện khoảng tới 20 triệu phần tử đầu tiên thì nhanh, nhưng từ sau đó lại chậm, có
lẽ không quản lý nổi vùng nhớ nữa nên dẫn đến rất lâu B-Tree thì ngược lại, có thể tạo cây 100 triệu phần tử cũng không có gì khác thường.
CPU lúc chạy AVL(~99%) luôn cao hơn lúc chạy B-Tree(<50%).
4.Ứng dụng B-Tree
Như đã nói điểm mạnh của B-Tree là dùng cho các dữ liệu lớn, lưu trữ ở
bộ nhớ ngoài Và thực tế B-Tree được sử dụng rộng rãi trong hệ quản trị CSDL và hệ thống file:
Windows: HPFS.
Mac: HFS, HFS+.
Linux: ReiserFS, XFS, Ext3FS, JFS.
Databases: ORACLE, DB2, INGRES, SQL, PostgreSQL.
Một biến thế khác của B-Tree là Red-Black Tree cũng được ứng dụng rộng rãi trong các bảng ký hiệu hệ thống:
Java: java.util.TreeMap, java.util.TreeSet.
C++ STL: map, multimap, multiset.
Linux kernel: linux/rbtree.h.