Gửi vào thư cho mình:
[email protected] ĐỐNG NHỊ THỨC 1 Giới thiệu chung.............................................................................................2 2 Cây nhị thức và đống nhị thức......................................................................2 2.1 Đống nhị phân...........................................................................................2 2.2 Cây nhị thức..............................................................................................3 2.2.1 Định nghĩa cây nhị thức.....................................................................3 2.2.2 Đặc điểm cây nhị thức.......................................................................4 2.3 Đống nhị thức............................................................................................5 2.3.1 Định nghĩa.........................................................................................5 2.3.2 Tính chất............................................................................................6 2.3.3 Biểu diễn đống nhị thức.....................................................................6 3 Các thao tác trên đống nhị thức....................................................................8 3.1 Tạo một đống nhị thức..............................................................................8 3.2 Tìm khóa nhỏ nhất.....................................................................................8 3.3 Hợp hai đống nhị thức...............................................................................9 3.3.1 Liên kết hai cây nhị thức....................................................................9 3.3.2 Hòa nhập (Merge) hai đống nhị thức.............................................10 3.3.3 Hợp (Union) hai đống nhị thức........................................................12 3.4 Chèn một nút vào đống nhị thức.............................................................17 3.5 Tách ra nút có khóa nhỏ nhất..................................................................18 3.6 Giảm khóa...............................................................................................20 3.7 Xóa một khóa..........................................................................................23 4 Đánh giá và kết luận....................................................................................23
1
Giới thiệu chung
Đống nhị thức (Binomial heaps) được giới thiệu bởi Vuillemin [1]. Brown [2, 3], nghiên cứu chi tiết các đặc điểm của nó được giới thiệu trong cuốn "Introduction to Algorithms" [4]. Cấu trúc dữ liệu Đống nhị thức được gọi là Đống hợp nhất (Mergeable heaps) hỗ trợ thao tác sau: 1 Heap_empty() khởi tạo và trả về một đống mới rỗng. 2. Insert(Hp, x) chèn nút x, mà key của nó đã được điền vào đống Hp. 3. Find_min_key(Hp) trả về một con trỏ chỉ đến nút trong đống H mà trường key là nhỏ nhất. 4. Delete_min(Hp) tách ra nút từ đống Hp mà key là nhỏ nhất, trả về một con trỏ chỉ vào nút này. 5. Merge(Hp1, Hp2) tạo và trả về một đống mới có chứa tất cả các nút của đống Hp1 và Hp2. Các đống Hp1 và Hp2 được xóa bỏ bởi thao tác này. Ngoài ra, cấu trúc dữ liệu đống nhị thức được Vuillemin giới thiệu năm 1978 hỗ trợ thêm hai thao tác sau đây: 1. Decrease_key(Hp, x, k) gán cho nút x trong đống Hp giá trị mới k không lớn hơn giá trị hiện tại của x. 2. Delete(Hp, x) xóa nút x trong đống Hp. Như vậy, Đống nhị thức là một đống tương tự như một Đống nhị phân và còn hỗ trợ một cách nhanh chóng sát nhập hai đống. Điều này đạt được bằng cách sử dụng một cấu trúc cây đặc biệt. 2
Cây nhị thức và đống nhị thức.
2.1 Đống nhị phân
Một đống nhị phân (heap) là một cấu trúc cây nhị phân với hai tính chất sau: 1) Cây nhị phân đầy đủ hoặc gần như đầy đủ. 2). Khóa tại mỗi nút đều nhỏ hơn hoặc bằng khóa của các nút trong hai cây con của nó.
Hình 2-1. Ví dụ một đống nhị phân Min-heap
Chúng ta gọi heap được định nghĩa như trên là một Min- heap, để phân biệt với trường hợp Max-heap. Trong min-heap thì phần tử tại gốc là phần tử bé nhất. Maxheap sẽ sửa điều kiện thứ hai thành “Khóa tại mỗi nút đều lớn hơn hoặc bằng khóa của các nút trong hai cây con của nó”, do đó phần tử tại gốc sẽ là phần tử lớn nhất. Đống nhị phân được ứng dụng rộng rãi đặc biệt sử dụng cho hàng ưu tiên (Priority queue), ta có thể thực hiện đơn giản các phép thêm phần tử, loại bỏ phần tử, tìm phần tử nhỏ(lớn) nhất, tăng giảm độ ưu tiên, nhưng thao tác trộn các đống nhị phân thực hiện không hiệu quả vì việc đòi hỏi luôn phải di chuyển các phần tử. Để khắc phục hạn chế trên người ta đưa ra các cấu trúc khác như: - d-heaps: hoàn toàn giống như đống nhị phân ngoại trừ mỗi nút có d trứ không phải 2 con, d càng lớn thì càng lợi cho phép thêm vào. Heap lệch trái (Leftist heaps): cũng giống như đống nhị phân nhưng tại mỗi nút chiều dài đường đi đến Null của con bên trái lớn hơn hoặc bằng chiều dài đường đi đến Null của con bên phải, thuận lơi cho phép trộn với chi phí là O(logN). - Skew heap: giống như heap lệch trái nhưng không chứa thông tin về đường đi tới Null và không có ràng buộc gì về chiều dài đường đi phải. Như vậy trong trường hợp xấu nhất các tác vụ chi phí đến O(N), khi cây nhị phân suy biến thành chuỗi mắc xích N nút về bên phải. 2.2 Cây nhị thức 2.2.1 Định nghĩa cây nhị thức.
Cây nhị thức Bk với k = 0, 1, 2,… là một cây có thứ tự được định nghĩa đệ quy: 1). Cây nhị thức B0 gồm một nút duy nhất. 2). Cây nhị thức Bk gồm hai cây nhị thức Bk-1 được liên kết với nhau theo một cách nhất định: * Nút gốc của cây này là con bên trái nhất của nút gốc của cây kia.
Hình 2-2. Định nghĩa đệ quy của cây nhị thức Bk
Hình 2-3. Ví dụ biểu diễn các cây nhị thức B0, B1, B2, B3, B4
Hình 2-4. Một cách nhìn khác của cây nhị thức Bk
2.2.2 Đặc điểm cây nhị thức. a). Bổ đề:
Cây nhị thức Bk có các tính chất sau: 1). có 2k nút, 2). chiều cao của cây là k, 3). có đúng
C
k i
nút tại độ sâu i với i = 0, 1,..., k
4). bậc của nút gốc của cây là k, nó lớn hơn bậc của mọi nút khác; ngoài ra nếu các con của nút gốc được đánh số từ trái sang phải bằng k - 1, k - 2,..., 0, thì nút con i là gốc của cây con Bi . Chứng minh: 1) Dùng phương pháp quy nạp toán học theo k:
B0
B1
B2
Bước cơ bản: dễ dàng thấy các tính chất là đúng cho B0 có 1 nút, hiển nhiên đúng vì 20 =1 nút. Bước quy nạp: giả sử bổ đề là đúng cho B k-1 tức là có 2k-1 nút, cây nhị thức Bk gồm hai cây Bk-1 nên cây Bk có 2k-1 + 2k-1 = 2k nút. 2). Do cách liên kết hai cây nhị thức Bk-1 với nhau để tạo nên Bk nên độ sâu tối đa của nút trong Bk bằng độ sâu tối đa của nút trong B k-1 cộng thêm 1, tức là: (k - 1) + 1 = k. 3). Gọi D(k,i) là số các nút tại độ sâu i của cây nhị thức Bk, khi đó Bk bao gồm hai cây Bk-1 kết hợp với nhau, một nút ở độ sâu i trong Bk-1 xuất hiện trong Bk một lần ở độ sâu i và một lần ở độ sâu i+1, nghĩa là số nút ở độ sâu i trong B k sẽ bằng số nút ở độ sâu i trong Bk-1 cộng với số nút ở độ sâu i-1 trong Bk-1 .
Hình 2-5. Biểu diễn số nút tại độ sâu i của Bk k −1
k −1
k
D(k , i ) = D (k − 1, i ) + D (k − 1, i − 1) = C i + C i −1 = C i
4). Từ định nghĩa cây nhị thức và hình 2-4 ta nhận thấy trong cây Bk chỉ có một nút duy nhất cao hơn các nút trong Bk-1 chính là nút gốc, nút gốc cây Bk-1 có bậc là k1 thì nút gốc của Bk có bậc là k. Bằng phương pháp quy nạp toán học, từ hình 2-4 từ trái qua phải các con của nút gốc cây Bk-1 chính là gốc của các cây Bk-2, Bk-3, …, B1, B0. Khi Bk-1 liên kết với Bk-1 tao nên Bk khi đó các con của nút gốc chính là gốc của Bk-1, Bk-2, …, B1, B0. b) Hệ quả:
Bậc tối đa của một nút bất kỳ trong một cây nhị thức có n nút lá là log2(n). Chứng minh: Từ tính chất 4 của bổ đề ta có n=2k với k là bậc của nút gốc (là bậc tối đa), logarit hai vế ta được k=log2(n).
Độ sâu i -
2.3 Đống nhị thức 2.3.1 Định nghĩa.
Một đống nhị thức H là một tập các cây nhị thức thỏa mãn các tính chất sau: 1). Mọi cây nhị thức trong H là một đống có trật tự (heap-ordered): mọi nút đều có khóa lớn hơn hay bằng khóa của nút cha của nó.
2). Với mọi số nguyên k ≥ 0 cho trước thì có nhiều nhất một cây nhị thức trong H mà gốc của nó có bậc là k. Điều đó có nghĩa là một đống nhị thức không được phép có hai cây có cùng chiều cao. 2.3.2 Tính chất.
1). Gốc của một cây trong một đống nhị thức chứa khóa nhỏ nhất trong cây. 2). Một đống nhị thức H với n nút gồm nhiều nhất là log2(n) +1 cây nhị thức. Chứng minh: 1). Từ tính chất của đống (heaps) hiển nhiên ta có. 2). n có biểu diễn nhị phân duy nhất, biểu diễn này cần log2(n) +1 bits, có dạng 〈b
log2 n
n=
, b
log2 n -1
log 2 n
∑b 2 i =0
i
i
,..., b0〉 sao cho: 10 =
3
2
1
0
1010
Từ bổ đề 2.2.2, ta thấy cây nhị thức Bi xuất hiện trong H khi và chỉ khi bi = 1. như vậy đống nhị thức H chứa ít nhất log2(n) +1. Với n=10 thì trong heap có hai cây nhị thức là B1 và B3. 2.3.3 Biểu diễn đống nhị thức.
Để lưu trữ cho mỗi cây nhị thức trong một đống nhị thức ta sử dụng qui tắc: Biểu diễn theo kiểu “Bên trái là con, bên phải là anh em” (left-child, rightsibling representation). Mỗi nút x có các trường sau: - parent[x]: trữ con trỏ đến nút cha của x. - key[x]: trữ khóa của nút. - degree[x]: bậc của x (= số các con của x) - child[x]: con trỏ đến con bên trái nhất của x. Nếu x không có con thì child[x] = NIL - sibling[x]: con trỏ đến anh em của x ở ngay bên phải x. Nếu x là con bên phải nhất của cha của nó thì sibling[x] = NIL. parent key degree child sibling Hình 2-6. Biểu diễn các trường của nút x.
Danh sách gốc
Hình 2-7. Biểu diễn một đống nhị thức
Các gốc của các cây nhị thức trong một đống nhị thức được tổ chức thành một danh sách liên kết, gọi là danh sách các gốc của đống nhị thức. Khi duyệt danh sách các gốc của một đống nhị thức thì các bậc của các gốc theo thứ tự tăng dần. Nếu x là một gốc thì sibling[x] chỉ đến gốc kế đến trong danh sách các gốc. - Để truy cập một heap nhị thức H: head[H]: con trỏ chỉ đến gốc đầu tiên trong danh sách các gốc của H. head[H] = NIL nếu H không có phần tử nào. - Khai báo đống nhị thức: typedef struct heap_node_t { int key; unsigned int degree; struct heap_node_t* struct heap_node_t* struct heap_node_t* } heap_node;
parent; child; sibling;
3
Các thao tác trên đống nhị thức.
3.1 Tạo một đống nhị thức.
- Thủ tục để tạo một đống nhị thức mới: Make_Binomial_Heap 1.Tạo một đối tượng H với head[H] =NULL. Có thời gian chạy là O(1). heap_node* Make_Binomial_Heap() { heap_node* h; h = new heap_node_t; h = NULL; return h; }
- Khởi tạo một nút (node) với khóa là key: void Heap_Node_Init(heap_node *node, int key) { node->parent = NULL; node->child = NULL; node->sibling = NULL; node->key = key; node->degree = 0; } 3.2 Tìm khóa nhỏ nhất.
Thủ tục tìm một khóa nhỏ nhất trong một đống nhị thức H có n nút trả về con trỏ tới nút có khóa nhỏ nhất, giả sử rằng trong các nút không có nút nào có giá trị khóa là ∞. Vì đống nhị thức là tập hợp các cây nhị thức có tính chất đống (min_heap), do vậy ta chỉ tìm nút khóa nhỏ nhất trong danh sách gốc. Binomial_Heap_Min(H) 1). y := NIL; 2). x := head[H]; 3).
min := ∞;
4).
while x ≠ NIL do a). if key[x] < min then min := key[x]; y := x; b). x := sibling[x];
5).
return y;
Thời gian chạy của thủ tục là O(lg n) vì cần kiểm tra nhiều nhất là lg n + 1 nút gốc. Thủ tục cài đặt dưới đây trả về con trỏ node trỏ tới nút khóa nhỏ nhất và con trỏ prev đứng trước con trỏ node. void Binomial_Heap_Min(BinomialHeap *H, heap_node **prev, heap_node **node) { heap_node *prev_, *cur_; *prev = NULL; if (H->head == NULL) { *node = NULL; return; } *node = H->head; prev_ = H->head; cur_ = H->head->sibling; while(cur_) { if(cur_->key < (*node)->key) { *node = cur_; *prev = prev_; } prev_ = cur_; cur_ = cur_->sibling; } } 3.3 Hợp hai đống nhị thức
Thao tác hợp hai đống nhị thức là thao tác cơ bản nhất, được sử dụng như một chương trình con cho hầu hết các thao tác còn lại. Trước hết chúng ta xem xét vấn đề liên kết hai cây nhị thức. 3.3.1 Liên kết hai cây nhị thức.
Giả sử liên kết cây nhị thức nhị thức Bk - 1 có gốc tại nút y vào cây nhị thức Bk - 1 có gốc tại nút z để tạo ra cây nhị thức Bk . Nút z trở thanh gốc của một cây Bk . Binomial_Link(y,z) 1). parent[y] := z; 2). sibling[y] := child[z]; 3). child[z] := y; 4). degree[z] := degree[z] + 1
Thời gian chạy của thủ tục là O(1).
Hình 3-8. Liên kết hai cây nhị thức
Thủ tục cài đặt dưới đây trả về cây nhị thức khi thực hiện liên kết hai cây nhị thức T1 và T2. heap_node* Binomial_Link(heap_node* T1, heap_node* T2) { if(T1->key > T2->key) return Binomial_Link(T2,T1); T2->parent = T1; T2->sibling = T1->child; T1->child = T2; T1->degree = T1->degree + 1; return T1; } 3.3.2 Hòa nhập (Merge) hai đống nhị thức.
Thủ tục để hòa nhập danh sách các gốc của đống nhị thức H1 và danh sách các gốc của heap nhị thức H2 thành một danh sách các gốc duy nhất mà thứ tự các bậc là tăng dần .
Hình 3-9. Hòa nhập hai danh sách gốc H1, H2 theo thứ tự bậc tăng dần.
Binomial_Heap_Merge(H1,H2) 1). a = head[H1] 2). b = head[H2] 3). head[H1] = Min-Degree(a, b) 4). if head[H1] = NULL return 5). if head[H1] = b then b = a 6). a = head[H1]
y
z
Bk-1
Bk-1
H1 =
7).
while b NULL do a). if sibling[a] = NULL then sibling[a] = b return b). else if degree[sibling[a]] < degree[b] then a = sibling[a] else c = sibling[b] sibling[b] = sibling[a] sibling[a] = b a = sibling[a] b=c Nếu các danh sách các gốc của H1 và H2 có tổng cộng là m gốc, thì thời gian chạy của thủ tục là O(m). heap_node* Binomial_Heap_Merge( BinomialHeap *H1, BinomialHeap *H2) { heap_node* head, *a,*b; a = H1->head; b = H2->head; head = a; if(IsEmpty(H1)) // Heap H1 rỗng return b; if(IsEmpty(H2)) // heap H2 rỗng return a; if(a->degree > b->degree) return Binomial_Heap_Merge(H2,H1); while(b != NULL) { if(a->sibling == NULL) // có một cây { a->sibling = b; break; } else if(a->sibling->degree < b->degree) a = a->sibling; else{ heap_node* c = b->sibling; b->sibling = a->sibling; a->sibling = b; b=c; } } return head;
} 3.3.3 Hợp (Union) hai đống nhị thức.
Thủ tục hợp hai đống nhị thức co hai giai đoạn. Giai đoạn thứ nhất thực hiện bởi gọi thủ tục Binomial_Heap_Merge để kết hợp các danh sách các gốc của đống nhị thức H1 và H2 vào một danh sách liên kết H duy nhất được sắp xếp theo bậc đơn điệu tăng, nếu danh sách H nhân được là rỗng (do H1 và H2 đều rỗng) thì thuật toán dừng tại đây. Giai đoạn thứ hai, trên cơ sở danh sách liên kết H (có ít nhất một gốc) chúng ta có thể thực hiện tất cả các thao tác liên kết một cách nhanh chóng để H trở thành một đống nhị thức mới. Đầu tiên ta khởi tạo ba con trỏ và được duy trì trong suốt thủ tục: x trỏ vào gốc hiện đang được xét. prev_x trỏ tới gốc đứng trước x trong danh sách gốc sibling[prev_x] = x và next_x trỏ tới gốc đứng sau x trong danh sách gốc sibling [x] = next_x.
Hình 3-10. Khởi tạo các con trỏ trên danh sách gốc.
Ban đầu có nhiều nhất hai gốc có bậc bằng nhau trong danh sách gốc H (vì H1 và H2 đều là đống nhị thức) và chúng kế tiếp nhau trong danh sách. Nhưng thực tế trong quá trình hợp nhất có thể xuất hiện ba gốc có bậc bằng nhau kế tiếp, do vậy việc kết hợp x và next_x là phụ thuộc vào bậc của chúng và bậc của sibling [next_x]. Ta có các trường hợp sau: • Trường hợp 1: thể hiện trong hình 3-4 (a), xẩy ra khi degree[x] ≠ degree[next_x] co nghĩa là x là gốc của một cây Bk và next_x là gốc của cây Bl (l>k), chúng ta không liên kết x và next_x do vậy chi đơn giản là các con trỏ lên một vị trí trong danh sách gốc.
prev-x
x next-x
• Trường hợp 2: thể hiện trong hình 3-4 (b), xẩy ra khi xuất hiện ba gốc có bậc bằng nhau, nghĩa là degree[x] = degree[next_x] = degree[sibling[next_x]], chúng ta xử lý trường hợp này tương tự như trường hợp 1. • Trường hợp 3, 4: thể hiện trong hình 3-4 (c) và (d) xẩy ra khi chỉ degree[x] = degree[next_x]
≠
degree[sibling[next_x]],
chúng
ta
gọi
thủ
tục
Binomial_Link để liên kết x và next_x tùy thuộc vào key[x] ≤ key[next_x] hay key[x] > key[next_x], sau đó chúng ta đặt lại con trỏ như hình minh họa.
(a)
(b)
prev-x
a
prev-x
a
x
next-x sibling[ne
b
c
Bk
Bl
x
d
next-x sibling[ne
b
c
d
Bk
Bk
Bk
Hình 3-11. Các trường hợp xẩy ra khi liên kết danh sách gốc.
Ví dụ: Hợp hai đống nhị thức H1 và H2 sau
prev-x (c)head[H a 1]
x
b
12
next-x sibling[ne
c
d
7 25
28
Bl Bk Bk 41 key[x] ≤ key[next_x]
Hình 3-12. Hòa nhập danh sách gốc H1 với H2 thành danh sách gốc H sắp xếp đơn điệu tăng dần
1
3
head[H]
x
next-x
12
18
x head[H]
12 18
x
next-x
head[H]
prev-x 12 18
prev-x head[H]
12 18
Trư 15
Hình 3-13. Các trường hợp khi liên kết danh sách gốc H để nhận được đống nhị thức mới.
28
33
Binomial_Heap_Union(H1,H2) 1). H := Make_Binomial_Heap() 2). head[H] := Binomial_Heap_Merge(H1,H2) /* giải phóng H1 và H2 */ 3). if head[H] = NULL then return H 5). prev_x := NULL 6). x := head[H] 7). next_x := sibling[x] 8). while next_x NULL do a). if (sibling[next_x] NULL ) and (degree[x] degree[next_x]) or degree[sibling[next_x]] = degree[x]) then /* case 1 & 2 */ prev_x := x x := next_x b). else if key[x] head = Binomial_Heap_Merge (H1, H2); if(H->head == NULL) return H; prev_x = NULL; x = H->head; next_x = x->sibling; while(next_x != NULL) { if(((next_x->sibling!=NULL)&&(x->degree== next_x->sibling->degree ))|| (x->degree != next_x->degree)) // Case 1,2 { prev_x = x; x = next_x; } else if (x->key key) // Case 3 { x->sibling = next_x->sibling; Binomial_Link(x,next_x); } else { if (prev_x == NULL) // Case 4 H->head = next_x; else prev_x->sibling = next_x; Binomial_Link(next_x,x); x = next_x; } next_x = x->sibling; } return H; } 3.4 Chèn một nút vào đống nhị thức
Thủ tục để chèn một nút x vào một heap nhị thức H, giả sử đã dành chỗ cho x và khóa của x, key[x], đã được điền vào. Quá trình thực hiện thật đơn giản, bước đầu ta tạo một đống nhị thức H' bao gồm một nút x, sau đó ta thực hiện gọi thủ tục Binomial_Heap_Union để hợp H và H'. Binomial_Heap_Insert(H,x) 1). H' := Make-Binomial-Heap() 2). parent[x] := NIL 3). child[x] := NIL 4). sibling[x] := NIL 5). degree[x] := 0 6) head[H'] := x
7). H := Binomial_Heap_Union(H,H') Thời gian thực hiện tạo một đống nhị thức H' gồm một nút x là O(1), thời gian gọi Binomial_Heap_Union để hợp H' va H có n nút là O(lg n). vậy tổng thời gian là O(lg n). void Binomial_Heap_Insert(BinomialHeap **H, heap_node* x) { BinomialHeap *H1; Make_Binomial_Heap(&H1); x->parent = NULL; x->child = NULL; x->sibling = NULL; x->degree = 0; H1->head = x; *H = Binomial_Heap_Union(*H,H1); } 3.5 Tách ra nút có khóa nhỏ nhất
Thủ tục để tách ra nút có khóa nhỏ nhất khỏi heap nhị thức: đưa nút có khóa nhỏ nhất ra khỏi đống nhị thức H và trả về một con trỏ chỉ đến nút được tách ra. Binomial-Heap-Extract-Min(H) 1). Tìm nút gốc x với khóa nhỏ nhất trong danh sách gốc của Và xóa x ra khỏi danh sách gốc của H. 2). H' := Make_Binomial_Heap() 3). Đảo ngược thứ tự của các con của x trong danh sách liên kết của chúng, và gán vào head[H’] con trỏ chỉ đến đầu của danh sách có được 4). H := Binomial_Heap_Union(H,H') 5). return x Quy trình này hoạt động như thể hiện trong ví dụ hình 3-7. Đầu vào là đống nhị thức H được thể hiện trong hình 3-7 (a). Hình 3-7 (b) cho thấy tình hình sau khi nút gốc x với khóa nhỏ nhất đã được gỡ bỏ ra khỏi danh sách gốc của H. Nếu x là gốc của một Bk, theo tính chất đống nhị thức thì các con của x từ trái sang phải sẽ là gốc của các cây Bk-1, Bk-2, …, B1, B0. Hình 3-7 (c) thể hiện khi ta đảo ngược danh sách các con của x. Khi đó chúng ta sẽ nhận được một đống nhị thức H bao gồm phần còn lại đã loại bỏ x và đống nhị thức H' với danh sách gốc là các con của x đã được đảo ngược. Cuối cùng là gọi thủ tục Binomial_Heap_Union để hợp H và H'. Thời gian chạy của thủ tục là O(lg n) vì nếu H có n nút thì mỗi dòng từ 1 đến 4 thực thi trong thời gian O(lg n).
Ví dụ: Tách ra nút khóa nhỏ nhất
(a)
head[H]
(c) head[H]
10
37
Hình 3-14. Tách ra nút khóa nhỏ nhất
41
28
Hàm dưới đây trả về danh sách các nút đảo ngược khi xóa bỏ con trỏ cha. heap_node* Binomial_Heap_Reverse (heap_node* h) { heap_node *next; heap_node *tail = NULL; if(h == NULL) return h;
(b)
77
head[H]
13
}
h->parent = NULL; while(h->sibling != NULL) { next = h->sibling; h->sibling = tail; tail = h; h = next; h->parent = NULL; } h->sibling = tail; return h;
heap_node* Binomial_Heap_Extract_Min(BinomialHeap **H) { heap_node *prev, *node; BinomialHeap *H1; Binomial_Heap_Min (*H, &prev, &node); if(node == NULL) return NULL; if(prev != NULL) prev->sibling = node->sibling; else (*H)->head = node->sibling; Make_Binomial_Heap(&H1); H1->head = Binomial_Heap_Reverse(node->child); (*H) = Binomial_Heap_Union(*H,H1); return node; } 3.6 Giảm khóa
Thủ tục giảm khóa của một nút x trong đống nhị thức H đến giá trị k mới. Đầu tiên chúng ta kiểm tra giá trị k mới có lớn hơn giá trị khóa hiện hành không, nếu lớn hơn thông báo lỗi, ngược lại chúng ta thực hiện như sau: - Gán khóa mới cho nút x, đặt con trỏ y trỏ tới x và z trỏ tới cha của y (z = parent[y]). - Ta sẽ thực hiện kiểm tra đi ngược lên cho đến nút gốc của cây nhị thức chứa nó thì dừng: nếu gặp nút có giá trị khóa lơn thì đổi khóa (key[y] < key[z]) và đặt lại các con trỏ. Ví dụ: Giảm khóa nút có giá tri băng 26 tới 7 trong đống nhị thức H.
head[H]
Hình 3-15. Các bước thược hiện giảm khóa từ 26 tới 7
Binomial_Heap_Decrease_Key(H,x,k) 1). if k > key[x] then error "khóa mới lơn hơn khóa hiện hành" 2). key[x] := k 3). y := x 4). z := parent[y] 5). while z NULL and key[y] < key[z] do Đổi khóa ( key[y], key[z]) y := z z := p[y] Thời gian chạy của thủ tục là O(lg n): vì x có độ sâu tối đa là lg n nên vòng lặp while (dòng 5) lặp tối đa lg n lần. void Node_Swap(heap_node **node1, heap_node **node2) { int tg; tg = (*node1)->key; (*node1)->key = (*node2)->key; (*node2)->key = tg; } void Binomial_Heap_Decrease_Key(BinomialHeap **heap, heap_node *node, int new_key) { heap_node *temp, *parent; if( new_key >= node->key) { printf("Key moi lon hon"); return; } Else { node->key = new_key; temp = node; parent = temp->parent; while((parent!=NULL)&&(temp->keykey)) { Node_Swap(&temp,&parent); temp = parent; parent = temp->parent; } } }
3.7 Xóa một khóa
Giả định rằng không có nút nào trong đống nhị thức có giá tri khóa là - ∞. Để xóa một khóa trong đống nhị thức H chúng ta đầu tiên giảm khóa tới - ∞, sau đó gọi thủ tục tách ra nút khóa nhỏ nhất. Thời gian chạy của thủ tục là O(lg n) Binomial_Heap_Delete(H,x) 1).
Binomial_Heap_Decrease-Key(H,x, - ∞)
2).
Binomial_Heap_Extract_Min(H)
void Binomial_Heap_Delete(BinomialHeap **heap, heap_node *node) { Binomial_Heap_Decrease_Key(heap,node,-NOT_IN_HEAP); Binomial_Heap_Extract_Min(heap); }
4
Đánh giá và kết luận
Qua phân tính ở trên chúng ta có bảng đánh giá thời gian thực hiện các thao tác trên đống nhị thức với một số cấu trúc đống khác dưới bảng sau, giả sử số nút của đống là n. Binary heap Binomial heap Fibonacci heap (worst-case) (worst-case) (khấu hao) MAKE-HEAP O(1) O (1) O (1) INSERT O (lg n) O (lg n) O (1) MINIMUM O (1) O (lg n) O (1) EXTRACT-MIN O (lg n) O (lg n) O (lg n) UNION O (n) O (lg n) O (1) DECREASE-KEY O (lg n) O (lg n) O (1) Cùng với Fibonacci heap, Binomial heap là cấu trúc dữ liệu có ứng dụng trong các bài toán đồ thị, đặc biệt là tìm cây khung tối thiểu. Các thao tác trên cấu trúc này đều có độ phức tạp O(lg n). Ý tưởng chính của cấu trúc Binomial heap là chia một cây nhị phân đầy đủ thành đống các khối có kích thước 2k và đảm bảo các khối có chiều cao nhỏ có thể kết hợp với khối lớn hơn tiếp theo. Mỗi khối là một cây nhị thức gốc có khóa nhỏ nhất. Với cấu trúc này, chúng ta có thể hợp hai đống bằng việc thêm nhị phân trên danh sách các khối. Tuy nhiên cũng có nhiều cách khác để biểu diễn Binomial heap. Ta có thể thay đổi kích thước chuẩn miễn là chúng ta chỉ ra cấu trúc khối và kết hợp các khối có kích thước bằng nhau thành một khối tiếp theo có kích thước lớn hơn. Thủ tục
Tài liệu tham khảo
[1] Jean Vuillemin. "A data structure for manipulating priority queues", Communications of the ACM, 21(4):309-315, 1978 [2] Mark R. Brown. "The Analysis of a Practical and Nearly Optimal Priority Queue", PhD thesis, Computer Science Department, Stanford University, 1977, Technical Report STANCS-77-600. [3] Mark R. Brown. "Implementation and analysis of binomial queue algorithms". SIAM Journal on Computing, 7(3):298- 319, 1978. [4] T.H. Cormen, C.E. Leiserson, R.L.Rivest, C.Stein, "Introduction to Algorithms", Second Edition, the MIT Press, McGraw-Hill, 2001 [5] Peter Brass, Advanced Data Structures, Cambridge University Press, 2008