}; Lớp B_tree template struct B_node { // data members: int count; Record data[order − 1]; B_node *branch[order]; // constructor: B_node ; }; Giải thuật tìm kiếm template Error_code B_
Trang 1Kỹ thuật lập trình
Chương 4 – Các cấu trúc
dữ liệu tiên tiến
1 Tìm kiếm ngoài – B-cây
(Kruse, Chương 11.3)
1.1 Định nghĩa
Lưu trữ ngoài
Cho đến nay, ta luôn giả định là có thể lưu trữ toàn bộ một cấu trúc dữ liệu trong bộ nhớ trong
Nếu dữ liệu là quá lớn không thể vừa với bộ nhớ trong?
ÆSử dụng bộ nhớ ngoài
Vấn đề: đánh giá độ phức tạp về thời gian sử dụng O-lớn giả định mọi thao tác có thời gian như nhau
ÆKhông thể áp dụng với thao tác truy cập đĩa
Thời gian truy cập
Giả sử đĩa quay với tốc độ: 3600 RPM
Æmột vòng quay: 1/60 giây = 16.7ms
Trung bình cần nửa vòng/truy cập = 8ms
Æ1 giây = 120 lần truy cập = 25 triệu lệnh
1 phép truy cập đĩa = 200,000 lệnh
Thời gian truy cập
Thời gian truy cập:
Bộ nhớ trong: μs
Đĩa cứng: ms
Đĩa mềm: s
Đọc file:
Đọc theo từng trang / khối (block)
Block: 256-1024 từ
ÆTìm kiếm ngoài: tối thiểu hóa số lần truy cập đĩa
Trang 2Thời gian truy cập
Giả sử ta sử dụng một cây AVL để lưu trữ
toàn bộ thông tin chi tiết về người lái xe máy
tại VN (khoảng 20 triệu bản ghi)
Thu được cây rất cao:
hmin= log220,000,000 ~= 24
Æmất khoảng 0.2s
Giải pháp: sử dụng cây nhiều nhánh để giảm
độ cao
Cây tìm kiếm đa đường
Cây tìm kiếm nhị phân:
Mỗi nút có nhiều nhất 2 con
Giá trị nút cha > mọi nút của cây con trái
< mọi nút của cây con phải
Cây tìm kiếm đa đường cấp m
Mỗi nút có nhiều nhất m con
Một nút có k con và k-1 khóa
phân hoạch tất cả khóa trong cây con thành k tập con
(giải thích thêm: tham khảo trong Sedgewick hoặc Drozdek)
Mục đích:
có chiều cao nhỏ nhất
Điều kiện:
Không có cây con rỗng
Các nút lá cùng mức
Có ít nhất m/2 con
Để xây dựng cây có chiều cao nhỏ nhất:
Đảm bảo sao cho 0 có cây con rỗng xuất hiện trên
B-cây: Định nghĩa
Là cây tìm kiếm m-đường:
Tất cả nút lá có cùng mức
Nút giữa (trừ nút gốc) có: từ đến m nút
con
Số khóa của nút giữa = số con - 1,
Các khóa của nút giữa phân hoạch các khóa của
cây con như cây tìm kiếm
Nút gốc hoặc là nút lá hoặc có từ 2 đến m con
Nút lá chứa không nhiều hơn m - 1 khóa
⎡ m / 2 ⎤
Nên chọn m lẻ
Ví dụ: B-cây cấp 5
Trang 3Ví dụ: B-cây cấp 5
1.2 Thao tác thêm
Thêm phần tử vào B-cây
1. Tìm khóa cần thêm
2. Nếu không tìm thấy, thêm khóa vào nút lá tại
vị trí kết thúc
3. Nếu nút lá đầy:
1. Tách thành 2 nút
2. Xác định nút trung vị và gửi lên trên để chèn vào
nút cha
3. Lặp lại bước 2-3 với khóa trung vị cho đến khi gặp
nút gốc
4. Nếu nút gốc đầy thì tách thành 2 nút con và
nút gốc mới
Ví dụ
Thêm lần lượt dãy khóa sau vào một B-cây cấp 5 (ban đầu là cây rỗng):
a g f b k d h m j e s i r x c l n t u p
a g f b k d h m j e s i r x c l n t u p a g f b k d h m j e s i r x c l n t u p
Trang 4a g f b k d h m j e s i r x c l n t u p
1.3 Cài đặt giải thuật tìm kiếm
Cài đặt giải thuật
Mô phỏng: trên bộ nhớ trong
Sử dụng con trỏ
Thực tế: thay con trỏ bằng địa chỉ của một
khối/trang của đĩa
Khai báo lớp B_tree
template <class Record, int order>
class B_tree {
B_node<Record, order> *root;
// Add private auxiliary functions here.
};
Lớp B_tree
template <class Record, int order>
struct B_node {
// data members:
int count;
Record data[order − 1];
B_node<Record, order> *branch[order];
// constructor:
B_node( );
};
Giải thuật tìm kiếm
template <class Record, int order>
Error_code B_tree<Record, order> ::
search_tree(Record &target)
/* Post: If there is an entry in the B-tree whose key
matches that in target, the parameter target is replaced by the corresponding Record from the B-tree and a code of success is returned
Otherwise a code of not_present is returned.
Uses: recursive_search_tree */
{
return recursive_search_tree(root, target);
}
Trang 5template <class Record, int order>
Error_code B_tree<Record, order> :: recursive_search_tree
(B_node<Record, order> *current, Record &target)
/* Pre: current is either NULL or points to a subtree of
the B_tree
Post: If the Key of target is not in the subtree, a code of
not_present is returned Otherwise, a code of
success is returned and target is set to the
corresponding Record of the subtree.
Uses: recursive_search_tree recursively and
search_node */
recursive_search_tree {
Error_code result = not_present;
int position;
if (current != NULL) { result = search_node(current, target, position);
if (result == not_present)
result = recursive_search_tree(
current->branch[position], target);
else target = current->data[position];
}
return result;
}
search_node
template <class Record, int order>
Error_code B_tree<Record, order> :: search_node(
B_node<Record, order> *current, const Record &target, int
&position)
/* Pre: current points to a node of a B_tree.
Post: If the Key of target is found in *current, then a code of
success is returned,
the parameter position is set to the index of target, and the
corresponding
Record is copied to target Otherwise, a code of not_present is
returned,
and position is set to the branch index on which to continue the
search.
Uses: Methods of class Record */
search_node
{
position = 0;
while (position < >count && target >
current->data[position])
position ; // Perform a sequential search through the keys.
if (position < current->count && target == current->data[position])
return success;
else
return not_present;
}
1.3 Cài đặt giải thuật thêm
Thao tác thêm
Đệ quy
Tham số:
Đầu vào:
new_entry – bản ghi cần thêm
Đầu ra:
current: gốc của cây con hiện tại
median: bản ghi trung vị
right_branch: con trỏ tới nút nửa phải mới
Trang 6template <class Record, int order>
Error_code B_tree<Record, order> :: insert(const
Record &new_entry)
/* Post: If the Key of new_entry is already in the
B_tree, a code of duplicate_error
is returned Otherwise, a code of success is returned and the Record
new_entry is inserted into the B-tree in such a way that
the properties
of a B-tree are preserved.
Uses: Methods of struct B_node and the auxiliary
function push_down */
{
Record median;
B_node<Record, order> *right_branch, *new_root;
Error_code result = push_down(root, new_entry, median,
right_branch);
if (result == overflow) { // The whole tree grows in height.
// Make a brand new root for the whole B-tree.
new_root = new B_node<Record, order>;
new_root->count = 1;
new_root->data[0] = median;
new_root->branch[0] = root;
new_root->branch[1] = right_branch;
root = new_root;
result = success;
}
return result;
}
Giải thuật đệ quy thêm vào cây con
template <class Record, int order>
Error_code B_tree<Record, order> :: push_down(
B_node<Record, order> *current,
const Record &new_entry,
Record &median, B_node<Record, order> * &right_branch)
/* Pre: current is either NULL or points to a node of a B_tree.
Post: If an entry with a Key matching that of new_entry is in the subtree to
which current points, a code of duplicate_error is returned Otherwise,
new_entry is inserted into the subtree: If this causes the height of the
subtree to grow, a code of overflow is returned, and the Record median is extracted to be reinserted higher in the B-tree, together with the subtree
right_branch on its right If the height does not grow, a code of success is
returned.
Uses: Functions push_down (called recursively), search_node, split_node, and
push_in */
{
Error_code result;
int position;
if (current == NULL) {
// Since we cannot insert in an empty tree, the recursion terminates.
median = new_entry;
right_branch = NULL;
result = overflow;
}
else { // Search the current node.
if (search_node(current, new_entry, position) == success)
result = duplicate_error;
else {
Record extra_entry;
B_node<Record, order> *extra_branch;
result = push_down(current->branch[position], new_entry,
extra_entry, extra_branch);
if (result == overflow) {
// Record extra_entry now must be added to current
if (current->count < order − 1) {
result = success;
push in(current, extra entry, extra branch, position);
Thêm khóa vào nút
Trang 7template <class Record, int order>
void B_tree<Record, order> ::
push_in(B_node<Record, order> *current,
const Record &entry, B_node<Record, order>
*right_branch, int position)
/* Pre: current points to a node of a B_tree The node
*current is not full and
entry belongs in *current at index position.
Post: entry has been inserted along with its right-hand
branch right_branch into
*current at index position */
{
for (int i = current->count; i > position; i−−) { // Shift all later data to the right.
current->data[i] = current->data[i − 1];
current->branch[i 1] = current->branch[i];
}
current->data[position] = entry;
current->branch[position 1] = right_branch;
current->count ;
}
Tách đôi nút
template <class Record, int order>
void B_tree<Record, order> :: split_node(
B_node<Record, order> *current, // node to be split
const Record &extra_entry, // new entry to insert
B_node<Record, order> *extra_branch, // subtree on right of extra_entry
int position, // index in node where extra_entry goes
B_node<Record, order> * &right_half, // new node for right half of entries
Record &median) // median entry (in neither half)
/* Pre: current points to a node of a B_tree The node *current is full, but if there
were room, the record extra_entry with its right-hand pointer extra_branch
would belong in *current at position position, 0 position < order.
Post: The node *current with extra_entry and pointer extra_branch at position
position are divided into nodes *current and *right_half separated by a Record
median.
Uses: Methods of struct B_node, function push_in */
{
right_half = new B_node<Record, order>;
int mid = order/2; // The entries from mid on will go to right_half.
if (position <= mid) { // First case: extra_entry belongs in left half.
for (int i = mid; i < order − 1; i ) { // Move entries to right_half.
right_half->data[i − mid] = current->data[i];
right_half->branch[i 1 − mid] = current->branch[i 1];
}
current->count = mid;
right_half->count = order − 1 − mid;
push_in(current, extra_entry, extra_branch, position);
}
else { // Second case: extra_entry belongs in right half.
mid ; // Temporarily leave the median in left half.
for (int i = mid; i < order − 1; i ) { // Move entries to right_half.
right_half->data[i − mid] = current->data[i];
right_half->branch[i 1 − mid] = current->branch[i 1];
}
current->count = mid;
right_half->count = order − 1 − mid;
push_in(right_half, extra_entry, extra_branch, position − mid);
}
Tách đôi nút
TH1: position == 2, order == 5
(extra_entry nằm ở nửa trái)
Chuyển các phần tử sang phải
Trang 8Thêm extra_entry và extra_branch Xóa median, dịch branch
TH2: position == 3, order == 5
(extra_entry thuộc nửa phải) Chuyển phần tử sang phải
Thêm extra_entry và extra_branch Xóa median, dịch branch
Trang 91.3 Xóa nút trên B-cây
Giải thuật
Phần tử xóa nằm ở nút lá:
Nút lá có số phần tử > tối thiểu: xóa
Nút lá có số phần tử = tối thiểu: xét nút lá anh em kề
Nếu nút kề có số phần tử > tối thiểu: chuyển 1 phần tử lên nút cha, chuyển phần tử ở nút cha xuống nút lá có phần tử xóa.
Nếu nút kề có số phần tử = tối thiểu: phối hợp nút lá, nút kề và phần tử trung vị tại nút cha thành nút mới.
Nếu nút cha bị có quá ít phần tử Æ lan truyền lên trên.
Nút xóa nằm tại nút gốc, cây giảm chiều cao.
Phần tử xóa nằm ở nút giữa:
ÆPhần tử kế tục nằm tại một nút lá: thay thế phần
tử kế tục với phần tử xóa => xóa tại nút lá
Trang 10Kết quả