You are on page 1of 53

Đại Học Sư Phạm Tp.

Hồ Chí Minh

CẤU TRÚC DỮ LIỆU 2

Chương 02: BẢNG BĂM


Đề cương môn học: CTDL2
• Chương 1: Sắp xếp ngoại
• Chương 2: Bảng băm (Hash Table)
• Chương 3: B – Cây (B-Tree)
• Chương 4: Cây Đỏ Đen (Red-Black Tree)

CTDL2 – Lương Trần Hy Hiến 2


Nội dung
• Giới thiệu bài toán
• Hàm băm
• Các phương pháp xử lý đụng độ
• Phân tích phép băm

CTDL2 – Lương Trần Hy Hiến 3


ĐẶT VẤN ĐỀ
• Cho S là tập hợp n phần tử trong 1 cấu trúc
dữ liệu được đặc trưng bởi 1 giá trị khóa
• Tìm 1 phần tử có hay không trong S
– Tìm tuyến tính (O(n)), chưa được sắp xếp
– Tìm nhị phân (O(log2n)), đã được sắp xếp
• Có hay chăng 1 thuật toán tìm kiếm với O(1)
– Có, song ta phải tổ chức lại dữ liệu
– Dữ liệu được tổ chức lại là Bảng băm

CTDL2 – Lương Trần Hy Hiến 4


Giới thiệu về Bảng Băm
• Là CTDL trong đó các phần tử của nó được lưu trữ
sao cho việc tìm kiếm sẽ được thực hiện bằng cách
truy xuất trực tiếp thông qua từ khóa.
• Bảng băm có M vị trí được đánh chỉ mục từ 0 đến
M-1, M là kích thước của bảng băm.
• Các phương pháp băm:
– PP kết nối trực tiếp
– PP kết nối hợp nhất
– PP dò tuyến tính
– PP dò bậc 2
– PP băm kép

CTDL2 – Lương Trần Hy Hiến 5


Hàm băm
• Hàm băm: biến đổi khóa thành chỉ mục trên
bảng băm
– Khóa có thể là dạng số hay dạng chuỗi
– Chỉ mục được tính từ 0..M-1, với M là số chỉ mục
của bảng băm
– Hàm băm thường dùng: key % M, M với M là độ lớn
của bảng băm
• Hàm băm tốt phải thoả yêu cầu
– Giảm thiểu xung đột
– Phân bố đều trên M địa chỉ khác nhau của bảng
băm

CTDL2 – Lương Trần Hy Hiến 6


Mô tả dữ liệu
• K: tập các khoá (set of keys)
• M: tập các địa chỉ (set of addresses).
• HF(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. Thông thường HF(k) = k mod M

CTDL2 – Lương Trần Hy Hiến 7


Ưu điểm bảng băm
• Dung hòa tốt giữa thời gian truy xuất và
dung lượng bộ nhớ
– Nếu ko giới hạn bộ nhớ: one-to-one, truy xuất
tức thì
– Nếu dung lượng bộ nhớ có giới hạn thì tổ chức
khóa cùng địa chỉ
• Bảng băm ứng dụng nhiều trong thực tế,
thích hợp tổ chức dữ liệu có kích thước lớn
và lưu trữ ngoài.

CTDL2 – Lương Trần Hy Hiến 8


CÁCH XÂY DỰNG BẢNG BĂM
– Dùng hàm băm để ánh xạ khóa K vào 1 vị
trí trong bảng băm. Vị trí này như là 1 địa
chỉ khi tìm kiếm.

– Bảng băm thường là mảng, danh sách liên


kết, file(danh sách đặc)

CTDL2 – Lương Trần Hy Hiến 9


Ví dụ một bảng băm đơn giản

Khóa k sẽ được lưu trữ tại vị trí k mod M (M


kích thước mảng)

0 1 2 3 4 5 6 7 8 9
95

Thêm phần tử x = 95 vào mảng


95 mod 10 = 5

CTDL2 – Lương Trần Hy Hiến 10


Ví dụ một bảng băm đơn giản

Với các giá trị: 31, 10, 14, 93, 82, 95,79,18, 27, 46

0 1 2 3 4 5 6 7 8 9
10 31 82 93 14 95 46 27 18 79

CTDL2 – Lương Trần Hy Hiến 11


Tìm kiếm trên bảng băm
• Thao tác cơ bản nhất được cung cấp bởi
Hashtable là “tìm kiếm”
• Chi phí tìm kiếm trung bình là O(1), không
phụ thuộc vào số lượng phần tử của mảng
(Bảng)
• Chi phí tìm kiếm xấu nhất (ít gặp) có thể là
O(n)

CTDL2 – Lương Trần Hy Hiến 12


Các phép toán trên hàm băm
• Khởi tạo (Initialize)
• Kiểm tra rỗng (Empty)
• Lấy kích thước bảng băm (size)
• Thêm 1 phần tử vào bảng băm (Insert)
• Xóa 1 phần tử khỏi bảng băm (Remove)
• Duyệt (Traverse)

CTDL2 – Lương Trần Hy Hiến 13


Vấn đề nảy sinh

Giả sử thêm 55 vào mảng

0 1 2 3 4 5 6 7 8 9
82 95 27

+ 55 phải lưu vào vị trí 5. Tuy nhiên vị trí này đã


có chứa 95
k1 ≠ k2 mà f(k1) = f(k2)  Đụng độ
=> Cần giải quyết đụng độ (xung đột)
CTDL2 – Lương Trần Hy Hiến 14
Vấn đề xung đột khi xử lý bảng băm

- Trong thực tế có nhiều trường hợp có nhiều


hơn 2 phần tử sẽ được “băm” vào cùng 1 vị trí

- Hiển nhiên phần tử được “băm” đầu tiên sẽ


chiếm lĩnh vị trí đó, các phần tử sau cần phải
được lưu vào các vị trí trống khác sao cho vấn
đề truy xuất và tìm kiếm phải dễ dàng

CTDL2 – Lương Trần Hy Hiến 15


a. Làm giảm xung đột

-Hàm băm cần thỏa mãn các điều kiện:


Xác xuất phân bố khoá là đều nhau
Dễ dàng tính toán thao tác
Ít xảy ra đụng độ
Thông thường, hàm băm sử dụng các số nguyên
tố (vì xác suất ngẫu nhiên phân bố các số nguyên
tố là đều nhất)

CTDL2 – Lương Trần Hy Hiến 16


b. Giải quyết xung đột

Các phương pháp băm:


PP kết nối trực tiếp
PP kết nối hợp nhất
PP dò tuyến tính
PP dò bậc 2
PP băm kép

CTDL2 – Lương Trần Hy Hiến 17


i. Sử dụng DS liên kết (kết nối trực tiếp)

-Ý tưởng: “Các phần tử băm vào trùng vị trí k


được nối vào danh sách nối kết” tại vị trí đó

0 1 2 3 4
Hàm băm:
21 18 F(k) = k mod 5
Thêm
6, 16 6

16
CTDL2 – Lương Trần Hy Hiến 18
*Phân tích

PP DSLK có nhiều khuyết điểm:


- Khi có quá nhiều khoá vào cùng vị trí,
DSLK thì tại vị trí đó sẽ rất dài => Tăng chi phí
tìm kiếm
- Các ô trống còn dư nhiều => lãng phí về
thời gian tìm kiếm và không gian lưu trữ

CTDL2 – Lương Trần Hy Hiến 19


Cài đặt - Coding

- Sử dụng DSLK tại mỗi vị trí lưu trữ


- Nếu xảy ra xung đột thì lưu vào cuối DS
tại vị trí trùng

0 1 2 3 4
Hàm băm:
21 18 F(k) = k mod 5
Thêm
6, 16 6

16 CTDL2 – Lương Trần Hy Hiến 20


CÀI ĐẶT
• Khai báo cấu trúc dữ liệu
• Khởi tạo bảng băm
• Tạo 1 nút để thêm vào bảng băm
• Xác định hàm băm
• Thêm 1 nút vào bảng băm
• Xóa 1 phần tử trong bảng băm
• Tìm 1 phần tử trong bảng băm.

CTDL2 – Lương Trần Hy Hiến 21


Khai báo CTDL

const int M = 101; //Kích thước bảng băm


struct node
{
int key;
node* next;
};
typedef node* LINKLIST;
LINKLIST HashTable[M]; //bảng băm

CTDL2 – Lương Trần Hy Hiến 22


Khởi tạo bảng băm
void Init_HashTable()
{
for (int i = 0 ; i< M; ++ i)
HashTable[i] = NULL;
}
//Xác định hàm băm
int Hash(int key)
{
return (key % M);
}
CTDL2 – Lương Trần Hy Hiến 23
Tạo 1 nút để thêm
//tạo nút có trường dữ liệu là x, hàm trả về địa
chỉ của node mới tạo.
node* GetNode(int x)
{
node *pnew = new node;
pnew->key = x;
pnew->next = NULL;
return pnew;
}
CTDL2 – Lương Trần Hy Hiến 24
Kiểm tra bảng băm rỗng?
bool Empty()
{
for(int b = 0; b < M; b++)
if(HashTable[b] != NULL)
return false;
return true;
}

CTDL2 – Lương Trần Hy Hiến 25


Hàm xuất bảng băm
void PrintHT()
{
node *p;
for(int i=0;i<M;i++)
{
cout<<"Bucket "<<i<<": ";
p = HashTable[i];
while(p!=NULL)
{
cout<<“  “<<p->key;
p = p->next;
}
cout<<endl;
}
} CTDL2 – Lương Trần Hy Hiến 26
Thêm một khoá vào bảng băm
//Chèn một khoá vào bảng băm
void Insert(int k){
InsertTail_LINKLIST(HashTable[Hash(k)], k);
}
//Chèn cuối danh sách
void InsertTail_LINKLIST(LINKLIST &l, int k)
{
//Tạo node
node *add = GetNode(k);
if (l == NULL) l = add;
else
{
node * p = l;
while (p->next != NULL) p = p->next;
p->next = add;
}
} CTDL2 – Lương Trần Hy Hiến 27
Xoá một khoá trong bảng băm
//Hàm xóa 1 phần tử có khóa key
void Delete(int key)
{
Delete_LINKLIST(HashTable[Hash(key)], key);
}

CTDL2 – Lương Trần Hy Hiến 28


Xoá một khoá trong bảng băm
void Delete_LINKLIST(LINKLIST &l, int key)
{
node *p = l, *q = NULL;
if(p == NULL) //DS rong
cout<<endl<<"Khong tim thay - DS rong!"<<endl;
else //tim thay
{
if(p->key==key)//Nam dau danh sach
{
l = p->next;
delete p;
cout<<endl<<"Da xoa!"<<endl;
}

CTDL2 – Lương Trần Hy Hiến 29


Xoá một khoá trong bảng băm
else{
while((p != NULL)&&(p->key != key))
{
q = p; p = p->next;
}
if(p != NULL) //tim thay
{
q->next = p->next;
delete p;
cout<<endl<<"Da xoa!"<<endl<<endl;
}
else
cout<<"KQ: Khong tim thay"<<endl;
}
}
CTDL2 – Lương Trần Hy Hiến 30
}
Tìm một phần tử
// tim 1 phan tu co khoa =key trong bang bam
node* Search(int x)
{ int i = Hash(x);
node* p = HashTable[i];
if(p != NULL)
{
while((p != NULL)&&(p->key != x))
p = p->next;
}
return p;
}

CTDL2 – Lương Trần Hy Hiến 31


Hàm tạo menu chương trình
void menu()
{
puts("DEMO HASHTABLE dung DSLK");
puts("1. Them 1 phan tu vao bang bam");
puts("2. Xoa 1 phan tu trong bang bam");
puts("3. Tim 1 phan tu co khoa x");
puts("4. Xuat Hash Table");
puts("0. Ket thuc");
}

CTDL2 – Lương Trần Hy Hiến 32


Chương trình chính
int main()
{
int chon, x;
Init_HashTalbe();//khoi tao bang bam
do
{ menu();
cout<<"Chon chuc nang: ";
cin>>chon;
cout<<endl;
CTDL2 – Lương Trần Hy Hiến 33
Chương trình chính
switch(chon)
{
case 1:
do
{
cout<<"Nhap khoa can them: "; cin>>x;
if(x > 0) Insert(x);
}while(x > 0);
break;
case 2:
cout<<"Nhap khoa can xoa (key > 0): ";
cin>>x;
if(x > 0) Delete(x);
break;
CTDL2 – Lương Trần Hy Hiến 34
Chương trình chính
case 3:
cout<<"Nhap khoa can tim (key > 0): ";
cin>>x;
if(x > 0){
if(Search(x) != NULL)
cout<<"Tim thay “<<x;
else
cout<<"Tim khong thay";
}
break;
case 4: PrintHT(); break;
}
}
while(chon != 0);
return 0;
CTDL2 – Lương Trần Hy Hiến 35
}
ii. Sử dụng PP “kết nối hợp nhất”
-Ý tưởng: “Nếu có 1 khóa bị băm vào vị trí đã có
phần tử thì nó sẽ được chèn vào ô trống phía
cuối mảng”. (Dùng mảng có M phần tử)
0
1 21 Hàm băm:
Thêm F(k) = k mod 5
2 16
6, 16
3 18
4 6

CTDL2 – Lương Trần Hy Hiến 36


iii. Sử dụng PP “Dò tuyến tính”
-Ý tưởng: “Nếu có 1 khóa bị băm vào vị trí đã có
phần tử thì nó sẽ được chèn vào ô trống gần
nhất” theo phía bên phải (hoặc trái)
0 1 2 3 4
Hàm băm:
21 6 18 16 F(k) = k mod 5
Thêm
6, 16
f(key)=(f(key)+i) % M với f(key) là hàm băm chính của bảng băm.

CTDL2 – Lương Trần Hy Hiến 37


*Phân tích

- PP này dễ thực hiện


- Nếu có nhiều phần tử băm trùng nhau thì
đặc tính bảng băm bị mất đi
- Trong trường hợp xấu nhất tìm kiếm trên
bảng băm thành tìm kiếm tuyến tính trên mảng

CTDL2 – Lương Trần Hy Hiến 38


Cài đặt

-Khi xảy ra đụng độ thì chèn vào vị trí trống gần


nhất
- Ví dụ: chèn dãy: 5 16 7 8 2 4 6 3 13 24
vào mãng băm có 11 vị trí
0 1 2 3 4 5 6 7 8 9 10
24 2 3 4 5 16 7 8 6 13

CTDL2 – Lương Trần Hy Hiến 39


code

const int M = 101; //Kich thuoc bảng băm

int HashTable[M]; //bảng băm

CTDL2 – Lương Trần Hy Hiến 40


Khởi tạo bảng băm

void Init_HashTalbe()
{
for (int i = 0 ; i< M; ++ i)
HashTalbe[i] = -1 ;
}

CTDL2 – Lương Trần Hy Hiến 41


Kiểm tra bảng băng rỗng?
bool Empty()
{
for(int b = 0; b < M; b++)
if(HashTable[i] != -1)
return false;
return true;
}

CTDL2 – Lương Trần Hy Hiến 42


Thêm một khoá vào bảng băm
void Insert(int k)
{
//Xác định vị trí của khoá k
int pos = Hash(key)
//Kiểm tra đảm bảo vị trí pos là trống
while (HashTable[pos] != -1 )
{
pos = Hash (pos + 1);
//Kiểm tra full
}
HashTable[pos] = key;
}
CTDL2 – Lương Trần Hy Hiến 43
Xoá một khoá trong bảng băm
int Delete(int key)
{
int pos = Hash(key); count = 1;
while (HashTable[pos] != key){
pos = Hash(pos + 1 );
count ++;
if (count > M) break;
}
if (count <= M){ HashTable[pos] = -1; return 1;}

return 0; //không tìm được khoá


}
CTDL2 – Lương Trần Hy Hiến 44
iv. Sử dụng PP “Dò bậc hai”

f(key)=(f(key) + i2) % M
với f(key) là hàm băm chính của bảng băm.

0 1 2 3 4
Hàm băm:
16 21 6 18 F(k) = k mod 5
Thêm
6, 16

CTDL2 – Lương Trần Hy Hiến 45


iv. Sử dụng PP “Băm kép”

Ta sử dụng 2 hàm băm:


f1(key)= key % M
f2(key)= (M–2) – key % (M-2)

CTDL2 – Lương Trần Hy Hiến 46


Cài đặt – Băm kép (Double Hashing)

-Dò tuyến tính: pos = (pos + 1) % M


- Băm kép: pos = (pos + u) % M
u = Hash2 (key)

CTDL2 – Lương Trần Hy Hiến 47


code

const int M = 101; //Kich thuoc bảng băm

int HashTable[M]; //bảng băm

CTDL2 – Lương Trần Hy Hiến 48


Khởi tạo bảng băm

void Init_HashTalbe()
{
for (int i = 0 ; i< M; ++ i)
HashTalbe[i] = -1 ;
}

CTDL2 – Lương Trần Hy Hiến 49


2 Hàm băm

int Hash(int key)


{
return key % M
}

int Hash2(int key)


{
return (M-2) – key % (M-2);
}

CTDL2 – Lương Trần Hy Hiến 50


Thêm một khoá vào bảng băm
void Insert(int k)
{ //Xác định vị trí của khoá k
int pos = Hash(key)
//Kiểm tra đảm bảo vị trí pos là trống
while (HashTable[pos] != -1 ){
int u = Hash2 (key);
pos = (pos + u ) % M;
//Kiểm tra full
}
HashTable[pos] = key;
}

CTDL2 – Lương Trần Hy Hiến 51


Bài tập
Viết chương trình hiện thực từ điển Anh - Việt. Mỗi nút của
bảng băm có khai báo các trường sau:
– Trường word là khoá chứa một từ tiếng anh.
– Trường mean là nghĩa tiếng Việt.
– Trường next là con trỏ chỉ nút kế nếu bị xung đột.
Tập khoá là một chuỗi tiếng anh, tập địa chỉ có 26 chữ cái.
Chọn hàm băm sau cho khoá bắt đầu bằng ký tự a được băm
vào địa chỉ 0, b băm vào địa chỉ 1,…, z băm vào địa chỉ 25.
Chương trình có những chức năng như sau:
1. Nhập vào một từ
2. Xem từ điển theo ký tự đầu.
3. Xem toàn bộ từ điển.
4. Tra từ điển.
5. Xoá một từ, xóa toàn bộ từ điển.
CTDL2 – Lương Trần Hy Hiến 52
Câu hỏi và thảo luận

CTDL2 – Lương Trần Hy Hiến 53

You might also like