- Hàng ưu tiên.
5- Xoá một phần tử trong từ điển
Xoá một phần tử x trong từ điển, ta làm như sau: - Tính giá trị băm h(x)
- Kiểm tra xem phần tử x có thuộc “lớp” được trỏ bởi T[h(x)] không? Nếu có thì loại bỏ phần tử này. Khi loại x cần phân biệt x nằm ở đầu “lớp” và x không nằm ở đầu “ lớp”.
Gi
ải th u ậ t n h ư sau
Procedure Delete(x: KeyType; Var T : Dictionary); Var i: 0 .. N-1; P, Q : pointer; Found : Boolean; Begin i := h(x); if T[i]<>nil then
if T[i]^.key = x then {loại x khói danh sách} T[i] := T[i]^.Next;
Else Begin
{xem xét các thành phần tiếp theo trong danh sách} Q := T[i];
P := Q^.Next; Found := false;
While(P <>nil) and(not found) do If P^. Key= x then
Begin {loại x khỏi d.s} Q^.Next := P^.Next; Found := true; End Else Begin Q := P; P := Q^.Next; End;
End; End;
5.5.3.2. Cài đặt từ điển bằng bảng băm đónga) Thế nào là bảng băm đóng? a) Thế nào là bảng băm đóng?
Bảng băm đóng lưu giữ các phần tử của từ điển ngay trong mảng (các phần tử của “rổ” i lưu trong chính phần tử thứ i của mảng) chứ không dùng mảng để lưu trữ các con trỏ trỏ tới đầu của các danh sách liên kết – “rổ”.
Tương tự như băm mở, trong bảng băm đóng “rổ” thứ i chứa phần tử có giá trị băm là i, nhưng vì có thể có nhiều phần tử có cùng giá trị băm nên ta sẽ gặp trường hợp sau: ta muốn đưa vào “rổ” i một phần tử x nhưng “rổ” này đã bị chiếm bởi một phần tử y nào đó = > gâ y ra đụng độ. Như vậy khi thiết kế một bảng băm đóng ta phải có cách để giải quyết sự đụng độ này.
* Giải quyết đụng độ
Cách giải quyết đụng độ đó gọi là băm lại (rehash) như sau:
- Chọn tuần tự các vị trí h1,..., hk theo một cách nào đó cho tới khi gặp một vị trí trống để đặt x vào. Dãy h1,..., hk gọi là dãy các phép thử, k gọi là tổng số lần băm lại, hi là giá trị hàm băm của lần băm lại thứ i
- Có nhiều cách băm lại. Một chiến lược đơn giản là băm lại tuyến tính, trong đó dãy các phép thử có dạng :
Ví dụ N=8 và các phần tử của từ điển là a,b,c,d có giá trị băm lần lượt là: h(a)=3, h(b)=0, h(c)=5, h(d)=3. Ta muốn đưa các phần tử này lần lượt vào bảng băm.
Khởi đầu bảng băm là rỗng, có thể coi mỗi “rổ’ chứa một giá trị đặc biệt Empty, Empty không bằng với bất kỳ một phần tử nào mà ta có thể xét trong tập hợp các phần tử muốn đưa vào bảng băm.
Ta đặt a vào “rổ” 3, b vào “rổ” 0, c vào “rổ” 5. Xét phần tử d, d có h(d)=3 nhưng “rổ” 3 đã bị a chiếm ta tìm vị trí h1(d)= (h(d)+1) mod N = 4 , vị trí này là một “rổ” rỗng ta đặt d vào.
0 1 2 3 4 5 6 7
b a d c
Hình 5.1 - Giải quyết đụng độ trong bảng băm đóng bằng chiến lược băm lại tuyến tính
Trong bảng băm đóng, phép kiểm tra một thành viên (thủ tục MEMBER (x,A)) phải xét dãy các “rổ” h(x),h1(x),h2(x),... cho đến khi tìm thấy x hoặc tìm thấy một vị
trí trống. Bởi vì nếu hk(x) là vị trí trống được gặp đầu tiên thì x không thể được tìm gặp ở một vị trí nào xa hơn nữa. Tuy nhiên, nói chung điều đó chỉ đúng với trường hợp ta không hề xoá đi một phần tử nào trong bảng băm. Nếu chúng ta chấp nhận phép xoá thì chúng ta qui ước rằng phần tử bị xóa sẽ được thay bởi một giá trị đặc biệt, gọi là Deleted, giá trị Deleted không bằng với bất kỳ một phần tử nào trong tập hợp đang xét vào nó cũng phải khác giá trị Empty. Empty cũng là một giá trị đặc biệt cho ta biết ô trống.
Ví dụ
- Tìm phần tử e trong bảng băm trên, giả sử h(e)=5. Chúng ta tìm kiếm e tại các vị trí 5,5,6. “rổ” 6 là chứa Empty, vậy không có e trong bảng băm.
- Tìm d, vì h(d)=3 ta khởi đầu tại vị trí này và duyệt qua các “rổ” 5,5. Phần tử d được tìm thấy tại “rổ” 5.
* Sử d ụng b ả ng b ă m đ ón g đ ể cà i đ ặt t ừ đ iể n
Dưới đây là khai báo và thủ tục cần thiết để cài đặt từ điển bằng bảng băm đóng. Để dễ dàng minh hoạ các giá trị Deleted và Empty, giả sử rằng ta cần cài đặt từ điển gồm các chuỗi 10 kí tự. Ta có thể qui ước: Empty là chuỗi 10 dấu + và Deleted là chuỗi 10 dấu *.