Giáo trình Cấu trúc dữ liệu và giải thuật - Chương 11: Hàng ưu tiên

Định nghĩa: Một hàng ưu tiên các phần tử kiểu T gồm các phần tử của T, kèm

các tác vụ sau:

1. Tạo mới một đối tượng hàng rỗng.

2. priority_append: Thêm một phần tử mới vào hàng, giả sử hàng chưa đầy

(tùy vào độ ưu tiên của phần tử dữ liệu mới nó sẽ được đứng ở một vị trí thích

hợp).

3. priority_serve: Loại một phần tử ra khỏi hàng, giả sử hàng chưa rỗng

(phần tử bị loại là phần tử đến lượt được xem xét theo quy ước độ ưu tiên đã

định).

4. priority_retrieve: Xem phần tử tại đầu hàng (phần tử sắp được xem xét).

 

pdf22 trang | Chuyên mục: Cấu Trúc Dữ Liệu & Giải Thuật | Chia sẻ: tuando | Lượt xem: 421 | Lượt tải: 0download
Tóm tắt nội dung Giáo trình Cấu trúc dữ liệu và giải thuật - Chương 11: Hàng ưu tiên, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
i cây B2 còn lại thành một cây B3. Vì H3 chưa có cây nhị thức B3 nên B3 cuối 
cùng này sẽ là thành phần của H3. 
 H1 H2 
Hình 11.10- Hai hàng nhị thức H1 và H2. 
Hình 11.11 - Kết hợp hai cây nhị thức B1 trong H1 và H2. 
Hình 11.12- Kết quả trộn H1 và H2 thành H3. 
Việc kết hợp hai cây nhị thức tốn O(1), với O(log N) cây nhị thức trong mỗi 
hàng nhị thức, việc trộn hai hàng nhị thức cũng tốn O(log N) trong trường hợp 
xấu nhất. Để tăng tính hiệu quả, chúng ta lưu các cây nhị thức trong mỗi hàng 
nhị thức theo thứ tự tăng dần của chiều cao các cây. 
Việc thêm một phần tử mới vào hàng nhị thức tương tự như đối với heap lệch 
trái: trộn hàng nhị thức có duy nhất phần tử cần thêm với hàng nhị thức đã có. 
Việc loại phần tử nhỏ nhất cũng đơn giản: tìm phần tử nhỏ nhất trong số các 
nút gốc của các cây nhị thức. Giả sử đó là cây Bk. Sau khi loại bỏ nút gốc của cây 
Bk, chúng ta còn lại các cây con của nó: B0, B1, Bk-1. Gọi rừng gồm các cây con 
này là H’, và H’’ là hàng nhị thức ban đầu không kể BK. Việc cuối cùng cần làm là 
trộn H’ và H’’. Chúng ta có thể tự kiểm tra rằng các phép thêm và loại này đều 
tốn O(log N) trong trường hợp xấu nhất. 
16 
18 
12 
21 24 
65 
14 
26 
23 
51 24 
65 
13 
14 
26 16 
18 
23 
51 24 
65 
13 12 
21 24 
65 
14 
26 16 
18 
23
Chương 11 – Hàng ưu tiên 
Giáo trình Cấu trúc dữ liệu và Giải thuật 298
Hình 11.13- Quá trình thêm các phần tử 1, 2,, 7 vào hàng nhị thức. 
1 
2 3 
4 
1 
1 2 1 
2 
1 
2 
3 1 
2 
3 
1 
2 
3 4 1 
2 
3 
4 
1 
2 3 
4 
5 1 
2 3 
4 
5 
1 
2 3 
4 
5 6 1 
2 3 
4 
5 
6 
1 
2 3 
4 
5 
6 
7 1 
2 3 
4 
5 
6 
7 
Chương 11 – Hàng ưu tiên 
Giáo trình Cấu trúc dữ liệu và Giải thuật 299
 H 
 H’’ H’ 
Hình 11.14- Quá trình loại phần tử nhỏ nhất trong hàng nhị thức H. 
Hiện thực của hàng nhị thức 
Việc tìm phần tử nhỏ nhất cần duyệt qua các gốc của các cây nhị thức trong 
hàng nhị thức (12, 23 và 13 trong hình 11.14). Chúng ta có thể dùng danh sách 
liên kết để chứa các nút gốc này. Danh sách sẽ có thứ tự theo chiều cao của các 
cây nhị thức để phục vụ cho phép trộn hai hàng nhị thức được dễ dàng. 
Tương tự, các nút con của nút gốc của một cây nhị thức cũng được chứa trong 
một danh sách liên kết (14, 24 và 21 trong hình 11.14), để khi loại bỏ nút gốc 
(nút 12) thì phần còn lại cũng có cấu trúc tương tự như một hàng nhị thức mới, 
rất thuận lợi trong phép loại phần tử nhỏ nhất trong hàng nhị thức. 
Hình 11.15- Hàng nhị thức H 
23 
51 24 
65 
13 12 
21 24 
65 
14 
26 16 
18 
23 
51 24 
65 
13 21 24 
65 
14 
26 16 
18 
23 
51 24 
65 
13 
21 24 
65 
14 
26 16 
18 
23 
51 24 
65 
13 12 
21 24 
65 
14 
26 16 
18 
Chương 11 – Hàng ưu tiên 
Giáo trình Cấu trúc dữ liệu và Giải thuật 300
Hình 11.16- Hiện thực liên kết của hàng nhị thức H. 
Trong hình vẽ trên chúng ta thấy hình elipse nét rời biểu diễn các danh sách 
liên kết các nút mà chúng ta thường phải duyệt qua. Hình elipse nét rời lớn chứa 
các gốc của các cây nhị thức trong hàng nhị thức, khi cần tìm phần tử nhỏ nhất 
trong hàng nhị thức chúng ta tìm trong danh sách này. Hình elipse nét rời nhỏ 
chứa các nút con của nút gốc trong một cây nhị thức. 
Trong quá trình hình thành hàng nhị thức trong một số thao tác dữ liệu, khi 
kết hợp hai cây nhị thức có cùng chiều cao (hình 11.18), chúng ta cần nối một 
trong hai cây thành cây con của cây còn lại, mà cây con mới này cũng chính là 
cây con có chiều cao lớn nhất so với các cây con đã có. Việc chèn cây con mới vào 
đầu của danh sách liên kết sẽ thuận tiện hơn, vì vậy chúng ta cho danh sách này 
có thứ tự giảm dần theo chiều cao của các cây con (hình 11.18). 
Ngoài ra, mỗi nút trong cây nhị thức sẽ có một con trỏ cho phép truy xuất đến 
danh sách các con của nó. Tóm lại, hiện thực đơn giản và hiệu quả cho hàng nhị 
thức thật đơn giản như hình 11.18, đó là cấu trúc của một cây nhị phân, mỗi nút 
có con trỏ trái chỉ đến nút con đầu tiên của nó và con trỏ phải chỉ đến nút anh 
em của nó. 
Hình 11.17- Gốc các cây nhị thức được chứa trong mảng liên tục. 
Hình 11.17 là một phương án thay danh sách liên kết trên cùng bởi mảng liên 
tục. Chúng ta có thể dùng mảng liên tục cấp phát động để khắc phục nhược điểm 
do không biết trước chiều cao của cây nhị thức cao nhất trong hàng nhị thức. Việc 
dùng mảng liên tục cho phép tìm nhị phân phần tử nhỏ nhất, tuy nhiên sẽ là 
lãng phí lớn khi hàng nhị thức gồm quá ít cây nhị thức với nhiều chiều cao khác 
nhau. 
13 12 
14 24 
65 
21 
23 
51 24 
65 16 26 
18 
13 12 
14 24 
65 
21 
23 
51 24 
65 16 26 
18 
Chương 11 – Hàng ưu tiên 
Giáo trình Cấu trúc dữ liệu và Giải thuật 301
Hình 11.18- Kết hợp hai cây nhị thức B2 thành một cây nhị thức B3. 
Dưới đây là phần mã giả cho các khai báo cấu trúc của cây nhị thức và hàng 
nhị thức. Các tác vụ kết hợp hai cây nhị thức, trộn hai hàng nhị thức, và loại 
phần tử nhỏ nhất trong hàng nhị thức cũng được trình bày bằng mã giả. 
//Phần mã giả cho khai báo và một số tác vụ cho hàng nhị thức. 
struct Binomial_Node 
 DataType data 
 Binomial_Node* leftChild 
 Binomial_Node* nextSibling 
end struct 
struct Binomial_Tree 
 Binomial_Tree combineTrees(ref Binomial_Tree T1, ref Binomial_Tree T2) 
 Binomial_Node* root 
 int notEmpty() // Trả về 1 nếu cây không rỗng, ngược lại trả về 0. 
end struct 
class Binomial_Queue 
 public: 
 Binomial_Queue(Binomial_Node* p, int k)// k là số nút trong hàng nhị thức. 
 int empty 
 Binomial_Queue merge(ref Binomial_Queue H1, ref Binomial_Queue H2) 
 protected: 
 int count // Tổng số nút trong tất cả các cây nhị thức. 
 Binomial_Tree Trees[MAX] 
end class 
Binomial_Tree Binomial_Tree::CombineTrees(ref Binomial_Tree T1, 
ref Binomial_Tree T2); 
/* 
pre: T1 và T2 khác rỗng. 
post: T1 chứa kết quả kết hợp hai cây T1 và T2, T2 rỗng. Trả về cây T1. 
*/ 
{ 
1. if (T1.root->data > T2.root->data) 
1. Tráo T1 và T2 cho nhau 
2. T2.root->nextSibling = T1.root->leftChild // Chèn cây T2 vào đầu danh 
// sách các cây con của gốc của T1 
3. T1.root->leftChild = T2.root // Cập nhật nút con trái cho nút gốc của T1 
4. T2.root = NULL 
5. return T1 
} 
14 
16 26 
18 
12 
24 21 
65 
12 
24 21 
65 
14 
16 26 
18 
Chương 11 – Hàng ưu tiên 
Giáo trình Cấu trúc dữ liệu và Giải thuật 302
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
• 
Binomial_Queue Binomial_Queue::merge(ref Binomial_Queue H1, 
ref Binomial_Queue H2) 
/* 
post: H1 chứa kết quả trộn hai hàng nhị thức H1 và H2, H2 rỗng. Trả về hàng H1. 
uses: hàm Binomial_Tree::notEmpty() trả về 1 nếu cây không rỗng, ngược lại trả về 0. 
*/ 
{ 
 Binomial_Tree T1, T2, carry 
 int i = 0 
1. H1.count = H1.count + H2.count 
2. loop (H2 chưa rỗng hoặc carry không rỗng) 
1. T1 = H1.Trees[i] 
2. T2 = H2.Trees[i] 
3. case (T1.notEmpty() + 2*T2.notEmpty() + 4*carry.notEmpty()) 
1. case 0: // Không có cây nào 
2. case 1: // Chỉ có cây T1 
1. break 
3. case 2: // Chỉ có cây T2 
1. H1.Trees[i] = T2 
2. H2.Trees[i].root = NULL 
3. break 
4. case 4: // Chỉ có cây carry 
1. H1.Trees[i] = carry 
2. carry.root = NULL 
3. break 
5. case 3: // Có cây T1 và T2 
1. carry = combineTrees(T1, T2) 
2. H1.Trees[i].root = NULL 
3. H2.Trees[i].root = NULL 
4. break 
6. case 5: // Có cây T1 và carry 
1. carry = combineTrees(T1, carry) 
2. H1.Trees[i].root = NULL 
3. break 
7. case 6:// Có cây T2 và carry 
1. carry = combineTrees(T2, carry) 
2. H2.Trees[i].root = NULL 
3. break 
8. case 7:// Có cả 3 cây T1, T2, và carry 
1. H1.Trees[i] = carry 
2. carry = combineTrees(T1, T2) 
3. H2.Trees[i].root = NULL 
4. break 
4. endcase 
5. i = i + 1 
3. endloop 
4. return H1 
} 
Chương 11 – Hàng ưu tiên 
Giáo trình Cấu trúc dữ liệu và Giải thuật 303
Tóm lại, trong chương này chúng ta đã xem xét một số cách hiện thực của 
hàng ưu tiên. Heap nhị phân vừa đơn giản vừa hiệu quả vì không sử dụng con trỏ, 
nó chỉ hơi tốn nhiều vùng nhớ chưa sử dụng đến mà thôi. Chúng ta đã nghiên cứu 
thêm tác vụ trộn và bổ sung thêm ba phương án khác của hàng ưu tiên. Heap lệch 
trái là một ví dụ khá hay về giải thuật đệ quy. Skew heap giống heap lệch trái 
nhưng đơn giản hơn, với hy vọng rằng các ứng dụng có tính ngẫu nhiên thì các 
trường hợp xấu nhất sẽ không thường xuyên xảy ra. Cuối cùng hàng nhị thức cho 
thấy một ý tưởng đơn giản mà lại giới hạn được chi phí cho giải thuật khá tốt. 
Binomial_Queue::Binomial_Queue(Binomial_Node* p, int k) 
/* 
pre: p là địa chỉ nút đầu tiên của một cấu trúc liên kết các cây nhị thức Bk,Bk-1,,B0. 
post: Hàng nhị thức mới được tạo thành từ các cây này. 
*/ 
{ 
1. count = (1 << (k+1)) –1 
2. loop (k >= 0) 
1. Trees[k] = p 
2. p = p->nextSibling 
3. Trees[k]->nextSibling = NULL// Cắt rời các cây con để đưa vào mảng liên 
// tục các cây nhị thức cho hàng nhị thức mới. 
4. k = k – 1 
3. endloop 
} 
Chương 11 – Hàng ưu tiên 
Giáo trình Cấu trúc dữ liệu và Giải thuật 304

File đính kèm:

  • pdfgiao_trinh_cau_truc_du_lieu_va_giai_thuat_chuong_11_hang_uu.pdf