Bảng băm được cài đặt bằng danh sỏch kề cú M phần tử, mỗi phần tử của bảng băm là một mẫu tin cú một trường key để chứa khoỏ của phần tử.. Bảng băm này được cài đặt bằng danh sỏch k
Trang 1Phần 1
Cấu trúc của bản băm
1 Cấu trúc dữ liệu bảng băm
- Tương tự như trong trường hợp cài đặt bằng phương phỏp nối kết hợp nhất và các phương pháp khác
- Phép băm ở đây cũng dựa trên ý tưởng chung: biến đổi giỏ trị khúa thành một số (xử lý băm) và sử dụng số này để đỏnh chỉ cho bảng
dữ liệu
- Cách xây dựng bảng băm và các phộp toỏn trờn bảng băm khác với các cấu trúc trước đây, như: mảng, danh sách, cây nhị phân, … phần lớn được thực hiện bằng cỏch so sỏnh giá trị của các phần tử của cấu trúc, vì vậy thời gian truy xuất khụng nhanh và phụ thuộc vào kớch thước của cấu trỳc Đặc biệt khi cần phải xử lý cỏc bài toỏn cú dữ liệu lớn và được lưu trữ ở bộ nhớ ngoài
- Để khắc phục nhược điểm đó, cấu trúc của bảng băm tổng quát sẽ giải quyết thông qua: tập khoá, tập địa chỉ, hàm băm
Tập khoá K Hàm băm Tập địa chỉ M
K: Tập cỏc khoỏ (set of keys)
M: Tập cỏc dịa chỉ (set of addresses)
h(k): Hàm băm dựng để ỏnh xạ một khoỏ k từ tập cỏc khoỏ
K
thành một địa chỉ tương ứng trong tập M
*
*
* *
•
•
•
•
Trang 22 Ph ép băm.
Phần lớn trong hầu hết cỏc ứng dụng, khoỏ được dựng như một cách thức để truy xuất dữ liệu Hàm băm được dựng để ỏnh xạ giỏ trị khúa vào một dóy cỏc địa chỉ của bảng băm Khúa cú thể là dạng số hay số dạng chuỗi Nếu như cú 2 khúa ki và kj nếu h(ki)=h(kj) thỡ hàm băm bị đụng
độ Do đó ta phải có cách nào đó để khắc phục sự đung độ đó
3 M ột số hàm băm phổ biến.
* Hàm Băm sử dụng Phương phỏp nhõn.
h(k) = [m*(k*A mod 1)]
k là khúa, m là kớch thước bảng, A là hằng số: 0 < A < 1
* Hàm Băm sử dụng Phương phỏp chia.
Dựng số dư: h(k) = k mod m
k là khoỏ, m là kớch thước của bảng
Do đó h(k) sẽ nhận: 0,1,2,…,m-1
Việc ta chọn m sẽ ảnh hưởng đến h(k)
* Bảng băm với phương phỏp dũ tuần tự.
Bảng băm được cài đặt bằng danh sỏch kề cú M phần tử, mỗi phần tử của bảng băm là một mẫu tin cú một trường key để chứa khoỏ của phần tử Khi khởi tạo tất cả trường key được gỏn NullKey
- Khi thờm phần tử cú khoỏ k vào bảng băm, hàm băm h(k) sẽ xỏc định địa chỉ i trong khoảng từ 0 đến M-1:
ã Nếu chưa bị xung đột thỡ thờm phần tử mới vào địa chỉ này
h
Trang 3ã Nếu bị xung đột thỡ hàm băm lại lần 1, hàm h1 sẽ xột địa chỉ kế tiếp, nếu lại bị xung đột thỡ hàm băm thỡ hàm băm lại lần 2, hàm h2 sẽ xột địa chỉ kế tiếp nữa, …, và quỏ trỡnh cứ thế cho đến khi nào tỡm được địa chỉ trống và thờm phần tử mới vào địa chỉ này
* Bảng băm với phương phỏp kết nối hợp nhất
Bảng băm này được cài đặt bằng danh sỏch kề, mỗi phần tử cú hai trường: trường key chứa khúa của phần tử và trường next chỉ phần tử kế
bị xung đột Cỏc phần tử bị xung đột được kết nối nhau qua trường kết nối next
* Bảng băm với phương phỏp dũ bậc hai
Khi thờm phần tử vào bảng băm này, nếu băm lần đầu bị xung đột thỡ sẽ dũ đến địa chi mới, ở lần dũ thứ i sẽ xột phần tử cỏch i2 cho đến khi gặp địa chỉ trống đầu tiờn thỡ thờm phần tử vào địa chỉ này
* Bảng băm với phương phỏp băm kộp
Bảng băm này dựng hai hàm băm khỏc nhau, băm lần đầu với hàm băm thứ nhất nếu bị xung đột thỡ xột địa chỉ khỏc bằng hàm băm thứ hai
* Bảng băm với phương pháp kết nối trực tiếp
4 Bảng băm với phương pháp kết nối trực tiếp
a ý tưởng:
1 Biến đổi giỏ trị khúa thành một số (xử lý băm) và sử dụng số này để đỏnh chỉ cho bảng dữ liệu
2 Bảng băm được cài đặt bằng cỏc danh sỏch liờn kết, cỏc phần tử trờn bảng băm được “băm” thành M danh sỏch liờn kết (từ danh sỏch 0 đến danh sỏch M–1)
3 Các giá trị khoá được phân vào từng cụm (bucket) là ứng với một địa chỉ băm, hay mỗi địa chỉ của bảng
Trang 4băm tương ứng một danh sỏch liờn kết, cỏc phần tử bị xung đột tại địa chỉ i được kết nối trực tiếp với nhau qua danh sỏch liờn kết i
b Mô tả:
+ Giả sử ta chọn M=10, các địa chỉ băm được đánh số từ 0 đến
9 Do đó các phần tử có hàng đơn vị tương ứng i (với i = 0 đến 9) sẽ được băm vào danh sách liên kết i
+ Mỗi phần tử của bảng băm gồm hai trường;
- Trường key: chứa khoá của mỗi phần tử
- Trường next: con trỏ chỉ đến phần tử kế tiếp, nếu có xung đột
+ Tập địa chỉ cụm i (0 đến M-1) có kích thước nhỏ thì có thể lưu trữ ở bộ nhớ trong, ngược lại phải lưu ở thiết bị nhớ ngoài và khối của bảng chỉ dẫn cụm (ở đầu mỗi danh sách liên kết i; i = 0 đến M-1) chứa con trỏ, trỏ đến giá trị đầu tiên của danh sách liên kết và sẽ được gọi vào bộ nhớ trong khi một giá trị băm i được tính
Ví dụ: Cỏc phần tử cú hàng đơn vị là 5 sẽ được băm vào danh sỏch liờn kết i = 5, cỏc phần tử cú hàng đơn vị là 8 sẽ được băm vào danh sỏch liờn kết i = 8
+ Khi thờm một phần tử cú khúa k vào bảng băm, hàm băm f(k)
sẽ xỏc định địa chỉ i trong khoảng từ 0 đến M-1 ứng với danh sỏch liờn kết i mà phần tử này sẽ được thờm vào
- Nếu chưa bị xung đột thỡ thờm phần tử mới vào địa chỉ này và con trỏ next nhận giá trị Nil
- Nếu bị xung đột thỡ phần tử mới được cấp phỏt là phần tử đứng trước p^.key nếu: k < p^.key ngược lại đứng sau p^.key nếu: k > p^.key (Điều này không nhất thiết phải như vậy vì việc
Trang 5ta làm như trên rất thuận lợi cho tìm kiếm sau này nếu cần Danh sách liên kết được sắp xếp theo giá trị các khoá tăng
dần.) Con trỏ liên kết next cũng được cập nhật lại sao cho cỏc
phần tử bị xung đột hỡnh thành một danh sỏch liờn kết
+ Khi tỡm một phần tử cú khúa k trong bảng băm, hàm băm f(k) cũng sẽ xỏc định địa chỉ i trong khoảng từ 0 đến M-1 ứng với danh sỏch liờn kết i cú thể chứa phần tử này Như vậy, việc tỡm kiếm phần tử trờn bảng băm sẽ được qui về bài toỏn tỡm kiếm một phần tử trờn danh sỏch liờn kết
+ Khi xoá một phần tử cú khúa k vào bảng băm, hàm băm f(k) cũng sẽ xỏc định địa chỉ i trong
khoảng từ 0 đến M-1 ứng với danh sỏch liờn kết i cú thể chứa phần tử này Như vậy, việc xoá phần tử trờn bảng băm sẽ được qui về bài toỏn xoá một phần tử trờn danh sỏch liờn kết
+ éể minh họa ta xột bảng băm cú cấu trỳc như sau:
- Tập khúa K: tập số tự nhiờn
- Tập địa chỉ M: gồm 10 địa chỉ (M={0, 1, …, 9}
- Hàm băm h(key) = key % 10
Chi tiết:
Ta có tập các khoá k: 10,21,70,52,22,63,33,44,91,15,26,25,47,48,39, …
0
M-1
Trang 6Tập địa chỉ M=10 được đánh số từ 0 đến M-1 (Từ 0 đến 9), ta được 10
danh sách liên kết khác nhau
Bảng băm đó "băm" các phần tử trong tập khoỏ K theo 10 danh sỏch liờn
kết khỏc nhau, mỗi danh sỏch liờn kết gọi là một bucket
Hàm băm h(k)=k mod M Ta chèn được các giá trị khoá k tương ứng với
từng danh sách liên kết
ã Bucket 0 gồm những phần tử cú khúa tận cựng bằng 0
Bucket 1 gồm những phần tử cú khúa tận cựng bằng 1
Bucket 2 gồm những phần tử cú khúa tận cựng bằng 2
0 1 2 3 4 5 6 7 8 9
7
8
1
M = 0
nil nil
nil
nil nil
nil nil
nil
nil
nil
nil
Trang 7ã Bucket i(i=0 | … | 9) gồm những phần tử cú khúa tận cựng bằng i.
+ Khi khởi động bảng băm, con trỏ đầu của cỏc bucket là NULL + Theo cấu trỳc này, với tỏc vụ insert, hàm băm h(k) sẽ được dựng
để tớnh địa chỉ của khoỏ k, tức là xỏc định bucket chứa phần tử và đặt phần tử cần chốn vào bucket này
Với tỏc vụ search, hàm băm sẽ được dựng để tớnh địa chỉ và tỡm phần tử trờn bucket tương ứng
+ i=h(k) => thuoc danh sach thu i (bucket[i])
+ Tìm kiếm khoá K trên danh sách bucket[i]
+ Khi kết thúc mỗi một danh sách (key cuối cùng của danh sách) con trỏ chỉ đến giá trị Nil
c Cỏc phộp toỏn:
+ Tớnh giỏ trị hàm băm: Giả sử chỳng ta chọn hàm băm dạng %: h(key)=key % M
+ Phộp toỏn initbuckets: khởi tạo cỏc bucket băng Null
+ Phộp toỏn emmptybucket(b): kiểm tra bucket b cú bị rỗng khụng? + Phộp toỏn emmpty: Kiểm tra bảng băm cú rỗng khụng?
+ Phộp toỏn insert: Thờm phần tử cú khúa k vào bảng băm
+ i=h(k)
+ kiểm tra bucket [i]: nếu rỗng => cung cấp nhớ cho bucket, gán khoá k
thêm phần tử có khoá k vào ds theo thứ tự tăng dần
+ Phộp toỏn remove: Xúa phần tử cú khúa k trong bảng băm
+ Phộp toỏn clear: Xúa tất cả cỏc phần tử trong bảng băm
+ Phộp toỏn traversebucket: Xử lý tất cả cỏc phần tử trong bucket b + Phộp toỏn traverse: Xử lý tất cả cỏc phần tử trong bảng băm
Trang 8+ Phộp toỏn search: Tỡm kiếm một phần tử trong bảng băm, nếu khụng tỡm thấy hàm này trả về hàm NULL, nếu tỡm thấy hàm này trả về địa chỉ của phần tử cú khúa k
B1: Tỡm danh sỏch liờn kết cú thể chứa khúa k
b = h(k);
p = bucket[b];
B2: Tỡm khúa k trong danh sỏch liờn kết p
5 Cài đặt bảng băm với phương pháp kết nối trực tiếp
a Khai báo cấu trúc bảng băm.
//kich thuoc cua bang bam
#define M 10 // M so nut co tren bang bam, du de chua cac nut nhap vao bang bam
//dinh nghia cau truc cua mot nut bang bam struct nodes
{ int key; //thanh phan 1 de luu khoa struct nodes *next; //thanh phan thu hai con tro luu dia chi cua phan
//tu ke tiep (xung dot hay la co cung gia tri du) };
//khai bao kieu con tro chi nut typedef struct nodes *NODEPTR;
//khai bao mang bucket chua M con tro dau cua Mbucket
NODEPTR bucket[M];
b Các tác vụ.
* Hàm băm.
Trang 9Giả sử chỳng ta chọn hàm băm dạng modulo: f(key)=key % M
//dua vao khoa va ket qua ra so du int hashfunc(int key)
{ return (key % M);
} Chỳng ta cú thể dựng một hàm băm bất kỡ thay cho hàm băm dạng % trờn
* Phộp toỏn khởi tạo (initbuckets):
Phộp toỏn này cho khởi động buckets: Khi khởi động
bảng băm, con trỏ đầu của cỏc bucket là NULL
//phep toan khoi tao buckets //gan cho cac pt cua mang gia tri con tro la NULL
void initbuckets() {
int i;
for(i=0;i<M;i++) bucket[i]=NULL;
}
* Phộp toỏn kiểm tra rỗng (empty):
Kiểm tra bucket cú rỗng khụng
//kiem tra bucket b co bi rong hay khong
int emptybucket(int b) {
if (bucket[b]==NULL) return TRUE;
else return FALSE;
} Kiểm tra bảng băm cú rỗng khụng
//kiem tra bang bam co rong hay khong?
int empty()
Trang 10{ int b;
for(b=0;b<M;b++)
if (bucket[b]!=NULL) return FALSE;
}
* Phộp toỏn chốn phần tử mới vào bảng băm (insert):
Thờm phần tử cú khúa k vào bảng băm
//phep toan insert //them vao mot key moi void insert(int k)
{ int b;
b=hashfunc(k);// ham hashfunc de tim so du place(b,k);//de tim vi tri chen
}
void place(int b,int k) {
NODEPTR p,q;//q la nut truoc,p la nut sau q=NULL;
for(p=bucket[b]
p!=NULL&&k>p->key;p=p->next) q=p;
if(q==NULL) push(b,k);
else insafter(q,k);
} //tac vu getnode NODEPTR getnode() {
NODEPTR p;
Trang 11p=(NODEPTR)malloc(sizeof(struct nodes)); return p;
} void push(int b,int x) {
NODEPTR p;
p=getnode();
p->key=x;
p->next=bucket[b];
bucket[b]=p;
} //Ham insafter them nut moi vao bucket sau nut p void insafter(NODEPTR p,int k)
{ NODEPTR q;
if (p==NULL) printf("Khong them nut moi duoc");
else { q=getnode();
q->key=k;
q->next=p->next;
p->next=q;
} }
* Phộp toỏn tỡm kiếm (search):
Để tỡm một phần tử cú khúa k trong bảng băm, hàm băm
f(k) sẽ xỏc định địa chỉ i trong khoảng từ 0 đến M-1 ứng với danh sỏch liờn kết i cú thể chứa phần tử này Như vậy, việc tỡm kiếm phần tử trờn bảng băm sẽ được qui về bài toỏn tỡm kiếm một phần tử trờn danh sỏch liờn kết
Nếu khụng tỡm thấy hàm này trả về hàm NULL, nếu tỡm
thấy hàm này trả về địa chỉ của phần tử cú khúa k
int search(int k) {
NODEPTR p;
Trang 12int b;
b=hashfunc(k);
p=bucket[b];
while (k>p->key && p!=NULL) p=p->next;
if (p==NULL| | k!=p->key) return 0;
else return 1;
}
• Phộp toỏn xoá một phần tử.
Để xoá một phần tử có giá trị khoá k, trước tiên hàm b
= h(k) và p = bucket[b] sẽ tìm danh sách liên kết mà có thể chứa khoá k
Nếu tìm thấy i = h(k) thuộc danh sách thứ i (bucket[i])
thì thực hiện việc tỡm khúa k trong danh sỏch liờn kết i Nếu tìm thấy khoá k thì thực hiện việc xoá khoá k và cập nhật lại con trỏ Nếu giá trị khoá k đó là duy nhất trong danh sách, khi đó sẽ đồng thời với việc giải phóng danh sách và tại bucket i đó sẽ nhận giá trị Null
//Ham xoa mot nut trong bang bam void remove(int k)
{ int b;
NODEPTR p,q;
b=hashfunc(k);
p=bucket[b];
q=p;
while(p!=NULL && p->key!=k) {
q=p;
p=p->next;
}
if (p==NULL) printf("\nKhong co nut co khoa %d",k);
Trang 13else if(p==bucket[b]) pop(b);
else delafter(q);
}
Phần 2
Đánh giá độ phức tạp của thuật toán
Ta có bảng băm T với m vị trí (m bucket) để lưu trữ n giá trị
Ta có hệ số phép tính trung bình về sự phân bố các giá trị là: α = n/m
* Thủ tục chính:
Chained-Hash-Insert (T, x)
Insert x đầu mỗi danh sách T[h(key[x])].
Thời gian xấu nhất để hoàn thành là – O(1).
Chained-Hash-Delete (T, x)
Xoá x khỏi danh sách T[h(key[x])].
Thời gian xấu nhất để hoàn thành, phụ thuộc vào độ dài của mỗi danh sách
Nếu là danh sách móc nối kép (đồng nghĩa với việc ta chọn m=n) thì sẽ là _ O(1)
Chained-Hash-Search (T, k)
Tìm khoá k trong danh sách T[h(k)].
Khoảng thời gian xấu nhất để hoàn thành, phụ thuộc vào độ dài của mỗi danh sách
Trang 14Nếu là danh sách móc nối kép (đồng nghĩa với việc ta chọn m=n) thì sẽ là _ O(1)
* Cụ thể.
Nếu ta coi như trung bình các khoá được phân bố trải đều như nhau trên danh sách thì ta có:
α = n/m
m: số bucket (Số các danh sách từ 0 đến m-1) n: Số giá trị của các khoá k được lưu trong danh sach liên kết
Trường hợp xấu nhất có thể sảy ra là:
O(n) + thời gian băm h(k)
Thời gian tìm kiếm còn phụ thuộc vào chiều dài của danh sách liên kết và vị trí tương đối của các khoá trong danh sách Do đó thời gian tìm kiếm trung bình, để tìm thấy hoặc không thấy một khoá k nào đó sẽ mất
khoảng thời gian
O(1 + α)
+ Ta đặt xi là phần tử thứ i, (ith) được chèn vào bảng băm và
đặt:
ki = key[xi].
+ Định nghĩa chỉ ra rằng: Nếu các giá trị khoá k có h(k) bằng
nhau (có cùng một địa chỉ băm thứ i) tùi cùng được lưu vào một danh sách liên kết
Xij = i{h(ki) = h(kj)}, với mọi i, j.
Xij: Chỉ ra vị trí của các khoá k tại địa chỉ thứ i
trong cùng một danh sách liên kết thứ j, (trong cùng bucket j, với j = 0, 1, 2, m-1) + Để đơn giản không giảm tính tổng quát với trường hợp này
ta có: