TỔNG QUAN VỀ CÁC GIẢI THUẬT TÌM KIẾM CƠ BẢN 1.1.Tìm kiếm tuyến tính Tìm kiếm tuyến tính[2], [3], [4], [9] hay tìm kiếm tuần tự là phương pháp tìm kiếm một phần tử cho trước trong một da
Trang 1BỘ GIÁO DỤC VÀ ĐÀO TẠO TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
-
NGUYỄN THỊ THANH VÂN
CÁC CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT HIỆU QUẢ CHO BÀI TOÁN TÌM KIẾM
Chuyên ngành: CÔNG NGHỆ THÔNG TIN
Trang 2LỜI CAM ĐOAN
Luận văn thạc sỹ này do em nghiên cứu và thực hiện dưới sự hướng dẫn của
Cô giáo TS Nguyễn Thị Thanh Huyền Với mục đích học tập, nghiên cứu để nâng
cao kiến thức và trình độ chuyên môn nên em đã làm luận văn này một cách nghiêm
túc và hoàn toàn trung thực
Để hoàn thành bản luận văn này, ngoài các tài liệu tham khảo đã liệt kê, em
cam đoan không sao chép toàn văn các công trình hoặc thiết kế tốt nghiệp của người
Trang 3LỜI CẢM ƠN
Đầu tiên, em xin gửi lời cảm ơn đến thầy, cô giáo Viện Công nghệ Thông tin
và Truyền thông đã tận tình dạy dỗ, dìu dắt chúng em trong suốt quá trình học tập tại Viện
Đặc biệt, em xin bày tỏ lòng biết ơn sâu sắc tới cô giáo Tiến sĩ Nguyễn Thị Thanh Huyền, Viện Toán ứng dụng và Tin học, Trường Đại học Bách khoa Hà Nội
đã tận tình hướng dẫn, đưa ra những góp ý, chỉnh sửa vô cùng quý báu cho em trong quá trình làm luận văn này
Cuối cùng, xin chân thành cảm ơn gia đình, bạn bè đã giúp đỡ, chia sẻ với
em trong quá trình học tập và nghiên cứu làm luận văn
Trang 4DANH MỤC CÁC HÌNH VẼ
Hình 1.1 Kết quả thực hiện chương trình với ví dụ 1.1……….……… 5
Hình 1.2 Kết quả thực hiện chương trình với ví dụ 1.2……….……… 9
Hình 1.3 Kết quả thực hiện chương trình với ví dụ 1.2……….………10
Hình 1.4 Thứ tự ưu tiên hàng và cột cho bảng chữ nhật ……….………11
Hình 1.5 Mảng truy xuất cho bảng chữ nhật ……….….… 13
Hình 1.6 Các bảng tam giác với nhiều dạng khác nhau ……… ….……14
Hình 1.7 Hiện thực liên tục của bảng tam giác ……….…… 15
Hình 1.8 Mảng truy xuất cho bảng lồi lõm ……… 16
Hình 1.9 Mảng truy xuất cho nhiều khóa bảng chuyển đổi ………18
Hình 1.10 Mô tả hàm băm ……….………19
Hình 1.11 Hàm băm dạng bảng tra được tổ chức dưới dạng danh sách kề 21
Hình 1.12 Kết quả thực hiện chương trình chèn khóa k vào bảng ………….… 30
Hình 1.13 Kết quả thực hiện chương trình trường hợp không tìm thấy khóa k trong bảng ………32
Hình 1.14 Kết quả thực hiện chương trình trường hợp không tìm thấy khóa k trong bảng ……… …… 33
Hình 2.1 Cây nhị phân đầy đủ ……….………36
Hình 2.2 Cây nhị phân không đầy đủ ……… 36
Hình 2.3 Các cây nhị phân suy biến ……… …… 37
Hình 2.4 Đánh số các nút của cây nhị phân đầy đủ biểu diễn bằng mảng ….….38
Hình 2.5 Biểu diễn cây nhị phân bằng cấu trúc liên kết ……….40
Hình 2.6 Cây nhị phân tìm kiếm ……….……… 43
Hình 2.7 Kết quả thực hiện chương trình tìm kiếm trên cây nhị phân……… 46
Hình 2.8 Sau khi thêm nút 6 vào cây nhị phân tìm kiếm ……….………… 48
Trang 5Hình 2.9 Kết quả thực hiện chương trình chèn thêm nút trên cây nhị phân… 50
Hình 2.10 Sau khi xóa nút 9 trên cây nhị phân tìm kiếm ……… … 51
Hình 2.11 Xóa nút có một nút con ……….……….… 52
Hình 2.12 Xóa nút có hai con ……….……….… 52
Hình 2.13 Kết quả thực hiện chương trình xóa nút lá trên cây nhị phân.…… 57
Hình 2.14 Kết quả thực hiện chương trình xóa nút có một cây con trên cây nhị phân ……… ….58
Hình 2.15 Kết quả thực hiện chương trình xóa nút có hai cây con trái và phải trên cây nhị phân ……… 58
Hình 2.16 Cây nhị phân cân bằng AVL……… … 59
Hình 3.1 Cấu trúc kiểu thứ bậc ……….……… ….64
Hình 3.2 Kiến trúc Lucene ……….……….…… … 69
Hình 3.3 Kiến trúc file Index ……….……… 70
Hình 3.4 Chỉ mục đảo ngược ….……….……… … 72
Hình 4.1 Ý nghĩa của mảng next ……….….……….….76
Hình 4.2 Ý nghĩa của mảng next tại vị trí m+1 ………….……….……76
Hình 4.3 Kết quả thực hiện thuật toán KMP_trường hợp tìm thấy chuỗi……….81
Hình 4.4 Kết quả thực hiện thuật toán KMPtrường hợp không tìm thấy chuỗi…82 Hình 4.5 Giao diện sử dụng của phần mềm ……… ……… 92
Hình 4.6 Kết quả tìm kiếm có giá trị là “Anh” ……… ……… 98
Hình 4.7 Kết quả tìm kiếm có giá trị là “Vân” ……… ……… 99
Hình 4.8 Kết quả tìm kiếm theo số điện thoại……… 100
Trang 6MỤC LỤC
MỞ ĐẦU 1
CHƯƠNG 1 TỔNG QUAN VỀ CÁC GIẢI THUẬT TÌM KIẾM CƠ BẢN 3
1.1.Tìm kiếm tuyến tính 3
1.2 Tìm kiếm nhị phân 5
1.3 Tìm kiếm thông tin trên bảng 10
1.3.1 Bảng chữ nhật 11
1.3.2 Các bảng tam giác 14
1.3.3 Các bảng lồi lõm 16
1.3.4 Các bảng chuyển đổi 17
1.4 Tìm kiếm theo phương pháp băm 18
1.4.1 Bảng băm 19
1.4.2 Hàm băm 20
1.4.3 Bảng băm mở 23
1.4.4 Bảng băm đóng 25
1.5 Phương pháp địa chỉ mở 27
1.6.Tìm kiếm xâu 34
CHƯƠNG 2 CÂY NHỊ PHÂN TÌM KIẾM 36
2.1 Cây nhị phân 36
2.1.1 Khái niệm cây 36
2.1.2 Khái niệm cây nhị phân 36
2.1.3 Biểu diễn cây nhị phân 38
2.2 Cấu trúc dữ liệu cây nhị phân tìm kiếm 43
2.2.1 Khái niệm cây nhị phân tìm kiếm 43
2.2.2 Các thao tác trên cây nhị phân tìm kiếm 43
2.3 Cây nhị phân cân bằng AVL 59
2.3.1 Khái niệm cây cân bằng 59
2.3.2 Cấu trúc dữ liệu biểu diễn cây nhị phân cân bằng 60
Trang 72.3.3 Các thao tác trên cây nhị phân cân bằng 60
2.4 Cây nhị phân tìm kiếm tối ưu 61
CHƯƠNG 3 TÌM KIẾM INDEX TRONG CƠ SỞ DỮ LIỆU 63
3.1 Khái niệm index 63
3.2 Index trong SQL Server 63
3.2.1 Giới thiệu 63
3.2.2 Các kiểu index 65
3.2.3.Phương pháp thiết kế index 67
3.2.4 Truy vấn với cấu trúc index 68
3.3 Index Lucene 69
3.3.1 Giới thiệu về Lucene 69
3.3.2 Kiến trúc Lucene 69
3.3.3 Kiến trúc File Index 70
3.3.4 Chỉ mục đảo ngược 72
CHƯƠNG 4 TÌM KIẾM XÂU MẪU 73
4.1 Bài toán đối sánh mẫu trong vấn đề tìm kiếm 73
4.1.1 Giới thiệu tổng quan về bài toán đối sánh mẫu trong vấn đề tìm kiếm 73
4.1.2 Bài toán đối sánh mẫu 74
4.1.3 So đơn mẫu 75
4.2 Thuật toán đối sánh mẫu KMP (Knuth Morris Pratt) 75
4.3 Thuật toán đối sánh mẫu BM (Boyer-Moore) 82
4.4 Thuật toán KMP mờ 85
4.4.1 Khái niệm tập mờ 85
4.4.2 Tiếp cận mờ cho bài toán tìm kiếm 87
4.4.3 Mô hình Otomat mờ so mẫu 87
4.4.4 Thuật toán KMP mờ 89
4.5 Ứng dụng thuật toán tìm kiếm xâu 92
4.5.1 Cài đặt ứng dụng 93
4.5.2 Kết quả tìm kiếm 98
Trang 8KẾT LUẬN 101 TÀI LIỆU THAM KHẢO 102
Trang 9MỞ ĐẦU
1 Lý do chọn đề tài
Máy tính ngày nay đã được sử dụng trong hầu hết tất cả các lĩnh vực và đã góp phần quan trọng vào việc thúc đẩy sự phát triển kinh tế, xã hội, khoa học kỹ thuật, … Máy tính ra đời nhằm phục vụ cho những mục đích nhất định của con người Từ thu thập thông tin đến xử lý thông tin, đặc biệt giúp con người trong việc tìm kiếm thông tin với khối lượng lớn, độ chính xác cao trong thời gian nhanh nhất
Ngày nay, kích thước của các hệ thống thông tin ngày càng lớn, trong khi nhu cầu tìm kiếm của người dùng đòi hỏi ngày càng cao và phức tạp Người dùng luôn mong muốn nhận được kết quả trả về trong thời gian nhanh, đáp ứng linh hoạt,
đa dạng các yêu cầu tìm kiếm Vậy nghiên cứu và ứng dụng Các cấu trúc dữ liệu
và giải thuật hiệu quả cho bài toán tìm kiếm là rất cần thiết
2 Mục đích nghiên cứu
- Nghiên cứu cấu trúc dữ liệu lưu trữ hiệu quả cho việc tìm kiếm
- Tìm hiểu các bài toán về tìm kiếm, nghiên cứu, cài đặt một số thuật toán tìm kiếm
3 Phạm vi nghiên cứu
Luận văn tập trung nghiên cứu các kỹ thuật cơ bản cho bài toán tìm kiếm, cấu trúc biểu diễn, các thao tác trên cây nhị phân tìm kiếm, tìm kiếm theo phương pháp băm, kỹ thuật index trong cơ sở dữ liệu, tìm kiếm xâu
Nội dung luận văn gồm có phần mở đầu, 4 chương, phần kết luận, tài liệu tham khảo và phụ lục
Chương 1 Tổng quan về các giải thuật tìm kiếm cơ bản Chương này trình
bày: Tìm kiếm tuyến tính, Tìm kiếm nhị phân, Tìm kiếm thông tin trên bảng, Tìm kiếm theo phương pháp băm, Phương pháp địa chỉ mở, Tìm kiếm xâu
Chương 2 Cây nhị phân tìm kiếm Chương này trình bày: khái niệm về cây,
cây nhị phân tìm kiếm, cấu trúc dữ liệu biểu diễn cây nhị phân tìm kiếm, các thao
Trang 10tác trên cây nhị phân tìm kiếm, khái niệm cây cân bằng AVL, cấu trúc dữ liệu biểu diễn cây nhị phân cân bằng, các thao tác trên cây nhị phân cân bằng và cây nhị phân tìm kiếm tối ưu
Chương 3 Tìm kiếm index trong cơ sở dữ liệu Chương này trình bày: về
khái niệm index, phương pháp thiết kế index và truy vấn với cấu trúc index Giới thiệu về Index Lucene, kiến trúc Lucene, kiến trúc File Index và chỉ mục đảo ngược
Chương 4 Tìm kiếm xâu mẫu Chương này trình bày: về tổng quan vấn đề
tìm kiếm, bài toán đối sánh mẫu trong vấn đề tìm kiếm - so đơn mẫu, một số thuật toán đối sánh mẫu điển hình: KMP (Knuth Morris Pratt), BM (Boyer-Moore), khái niệm tập mờ, tiếp cận mờ cho bài toán tìm kiếm, mô hình Otomat mờ so mẫu và thuật toán KMP mờ Luận văn cài đặt thuật toán KMP thử nghiệm xây dựng một ứng dụng tìm kiếm trên danh bạ điện thoại
Trang 11CHƯƠNG 1 TỔNG QUAN VỀ CÁC GIẢI THUẬT TÌM KIẾM CƠ BẢN
1.1.Tìm kiếm tuyến tính
Tìm kiếm tuyến tính([2], [3], [4], [9]) hay tìm kiếm tuần tự là phương pháp tìm kiếm một phần tử cho trước trong một danh sách bằng cách duyệt lần lượt từng phần tử của danh sách đó cho đến lúc tìm thấy giá trị mong muốn hoặc đã duyệt qua toàn bộ danh sách mà chưa thấy giá trị mong muốn
Bài toán Cho mảng a gồm n phần tử khác nhau: a1, a2, a3, , an và một phần tử X,
ta cần tìm vị trí i (1 ≤ i ≤ n) sao cho ai = X hoặc trả lại giá trị -1 nếu không có
phần tử như vậy trong mảng đã cho
Ý tưởng thuật toán
Thuật toán tiến hành so sánh phần tử đã cho trước với các phần tử thứ nhất, thứ hai, … của mảng cho đến khi thấy phần tử cần tìm hoặc đã tìm hết mảng mà không thấy phần tử
Thuật toán 1.1
Bước 1: i := 1; { Bắt đầu phần tử đầu tiên của dãy}
Bước 2: So sánh a[i] với x:
+ Nếu a[i] = x: Tìm thấy Dừng - trả về giá trị i
+ Ngược lại a[i] ≠ x: chuyển sang bước 3
Bước 3: i := i+1; {xét phần tử kế tiếp trong mảng}
Nếu i > N: Hết mảng, không tìm thấy Dừng - trả về giá trị -1 Ngược lại: Lặp lại Bước 2
Ví dụ 1.1 Cho dãy số a : 10, 6, 2, 14, 9, 15 Tìm x = 9
Trang 12Tìm thấy x = 9 tại vị trí i = 5 Dừng thuật toán
Độ phức tạp của thuật toán
Giải thuật tìm kiếm tuyến tính có độ phức tạp tính toán cấp n: T(n) = O(n)
Cài đặt thuật toán
int TimkiemTT(int x, int n, int a[])
Trang 13Bài toán Cho mảng a gồm n phần tử khác nhau đã đƣợc sắp xếp theo thứ tự tăng:
a1, a2, a3, …, an và một phần tử X, ta cần tìm vị trí i (1 ≤ i ≤ n) sao cho ai = X hoặc trả lại giá trị -1 nếu không có phần tử nhƣ vậy trong mảng đã cho
Trang 14Ý tưởng thuật toán
Trong mỗi bước, so sánh phần tử cần tìm với phần tử nằm ở chính giữa danh
sách Nếu hai phần tử bằng nhau thì phép tìm kiếm thành công và thuật toán kết
thúc Nếu chúng không bằng nhau thì tùy vào phần tử nào lớn hơn, thuật toán lặp lại
bước so sánh trên với nửa đầu hoặc nửa sau của danh sách Số lượng phần tử trong
danh sách cần xem xét sẽ giảm đi một nửa sau mỗi bước
Giả sử, dãy khóa đang xét là a1, a2, …, ar thì khóa ở giữa dãy sẽ là ai với
i = [ ] Tìm kiếm sẽ kết thúc nếu X = ai Nếu X < ai tìm kiếm sẽ được thực hiện
tiếp với a1, a2, …, ai-1; còn nếu X > ai tìm kiếm lại được làm với ai+1, …, ar Tiếp tục
sử dụng kỹ thuật tìm kiếm tương tự trên dãy sau Quá trình tìm kiếm được tiếp tục
cho đến khi tìm thấy khóa mong muốn hoặc không còn dãy khóa để xét
Thuật toán 1.2
Bước 1: Nhập N, các số hạng a1, a2, an và khóa X
Bước 2: left := 1; right := N; { tìm kiếm trên tất cả các phần tử}
Bước 3: mid := (left+right)/2; {lấy mốc so sánh}
{ So sánh a[mid] với x:}
+ Nếu a[mid] = X: Tìm thấy Dừng lại
+ Nếu a[mid] > X: { tìm tiếp x trong dãy con a[left] a[mid-1]:}
Trang 15Ở ví dụ trên thì lần duyệt thứ ba a[mid] = x Vậy chỉ số i cần tìm là i = mid = 6
- Trường hợp không tìm thấy khóa: Giả sử với n = 10, x = 25
Trang 16Cài đặt thuật toán
int TimkiemNP(int x, int n, int a[])
{ int left, right, mid;
} else { if (x > a[mid])
{
left = mid + 1; }
else {
right = mid - 1; }
} }
if (Found)
Trang 17{ return mid;
} else
{ return -1;
} }
Độ phức tạp của thuật toán
Giải thuật tìm kiếm nhị phân có độ phức tạp tính toán cấp log2n: T(n) = O(log 2 n)
Kết quả thử nghiệm
- Trường hợp tìm thấy khóa
Hình 1.2 Kết quả thực hiện chương trình với ví dụ 1.2
Trang 18- Trường hợp không tìm thấy khóa
Hình 1.3 Kết quả thực hiện chương trình với ví dụ 1.2
1.3 Tìm kiếm thông tin trên bảng
Tìm kiếm thông tin trên bảng([6]) là một phương pháp tìm kiếm bắt đầu từ một khóa và tìm một phần tử chứa khóa này Cách thực hiện và truy xuất các bảng trong vùng nhớ liên tục, bắt đầu từ các bảng hình chữ nhật thông thường, sau đó đến các bảng tam giác, bảng lồi lõm… Việc tra cứu bảng có thể đạt hiệu quả hơn nhiều so với bất kỳ phương pháp tìm kiếm nào do thời gian cần thiết là O(1) - có nghĩa là thời gian có giới hạn là một hằng số và độc lập với kích thước của bảng
Các phần tử của bảng mà chúng ta xem xét được đánh chỉ số bằng một mảng các số nguyên, tương tự cách đánh chỉ số của mảng
Trang 191.3.1 Bảng chữ nhật
- Thứ tự ưu tiên hàng và thứ tự ưu tiên cột:
Thứ tự ưu tiên hàng: Là đọc các phần tử ở hàng thứ nhất trước, từ trái sang phải, sau đó đến các phần tử hàng thứ hai và cứ thế tiếp tục cho đến đọc hàng cuối cùng
Thứ tự ưu tiên cột: Là đọc các phần tử ở cột thứ nhất trước từ trên xuống, sau đó đến các phần tử cột thứ hai và cứ thế tiếp tục cho đến đọc cột cuối cùng
Ví dụ 1.3 Giả sử có một bảng trừu tượng gồm 3 hàng, 4 cột và hàng được
đánh số là từ 1 đến 3 và cột được đánh số từ 6 đến 9 thì thứ tự của các phần tử theo thứ tự ưu tiên hàng như sau: (1,6) (1,7) (1,8) (1,9) (2,6) (2,7) (2,8) (2,9) (3,6) (3,7) (3,8) (3,9); thứ tự của các phần tử theo thứ tự ưu tiên cột như sau: (1,6) (2,6) (3,6) (1,7) (2,7) (3,7) (1,8) (2,8) (3,8)
Trang 20- Đánh chỉ số cho bảng chữ nhật:
Giả sử có bảng chữ nhật gồm m hàng, n cột và thứ tự hàng được đánh số từ 0 đến m – 1, và cột từ 0 đến n – 1 Số phần tử của bảng là m*n, đó cũng là số phần tử thực hiện liên tục trong mảng và được đánh số các phần tử trong mảng từ 0 đến m*n-1 Phần tử (0, 0) nằm tại vị trí 0, các phần tử thuộc hàng đầu tiên trong bảng (0, j) nằm tại vị trí j Phần tử đầu của hàng thứ hai (1, 0) nằm ngay sau phần tử (0, n – 1), đó là vị trí n Tiếp theo, phần tử (1, j) nằm tại vị trí n + j Các phần tử của hàng
kế tiếp cũng sẽ nằm sau số phần tử của hai hàng trước đó (2*n phần tử) Do đó phần
tử (2, j) nằm tại vị trí 2*n + j Tổng quát, các phần tử thuộc hàng i có n*i phần tử phía trước, nên suy ra công thức tính vị trí trong mảng nối tiếp mà một phần tử trong bảng chữ nhật được lưu trữ (hàm chỉ số) là: Phần tử (i, j) trong bảng chữ nhật nằm tại vị trí n*i + j trong mảng nối tiếp
- Mảng truy xuất:
Tính toán cho các hàm chỉ số của các bảng chữ nhật, các trình biên dịch của hầu hết các ngôn ngữ bậc cao sẽ dịch hàm này sang ngôn ngữ máy thành một số bước tính toán cần thiết Vì trên các máy tính nhỏ, phép nhân thường thực hiện rất chậm nên có thể sử dụng phương pháp khác để tránh phép nhân Phương pháp này lưu một mảng phụ chứa một phần của bảng nhân với thừa số là n:
0, n, 2*n, 3*n, … , (m-1)*n
Lưu ý, mảng này nhỏ hơn bảng chữ nhật rất nhiều nên nó có thể được lưu thường trực trong bộ nhớ Các phần tử của nó chỉ phải tính một lần (và chúng có thể được tính chỉ bằng phép cộng) Khi gặp một yêu cầu tham chiếu đến bảng chữ nhật, trình biên dịch có thể tìm vị trí của phần tử (i, j) bằng cách lấy phần tử thứ i trong mảng phụ cộng thêm j để đến vị trí cần có
Trang 221.3.2 Các bảng tam giác
Một bảng tam giác chỉ cần các địa chỉ (i, j) với i j
Cách biểu diễn bảng tam giác như Hình 1.6
Hình 1.6 Các bảng tam giác với nhiều dạng khác nhau
Để xây dựng hàm chỉ số mô tả cách ánh xạ này, giả sử các hàng, cột đều được đánh số bắt đầu từ 0 Để tìm vị trí của phần tử (i, j) trong mảng liên tục ta cần tìm vị trí bắt đầu của hàng i, sau đó để tìm cột j ta chỉ việc cộng thêm j vào điểm bắt đầu hàng i Nếu các phần tử của mảng liên tục được đánh số bắt đầu từ 0 thì chỉ số của điểm bắt đầu của hàng thứ i cũng chính là số phần tử nằm ở các hàng trên hàng
i Vậy trên hàng thứ 0 có 0 phần tử và chỉ có một phần tử của hàng 0 là xuất hiện trước hàng 1 Đối với hàng 2, có 1 + 2 = 3 phần tử đi trước và trường hợp tổng quát
ta thấy số phần tử có trước hàng i là: 1 + 2 + … + i = i *(i+1) Vậy phần tử (i, j) trong bảng tam giác tương ứng phần tử i*(i+1) + j của mảng liên tục
Trang 23Ví dụ 1.5 Giả sử có bảng tam giác sau:
Trang 24Tương tự như bảng chữ nhật, tránh mọi phép nhân và chia bằng cách tạo một mảng truy xuất chứa các phần tử tương ứng với các chỉ số của các hàng trong bảng tam giác Vị trí i trong mảng truy xuất là
i*(i + 1) Mảng truy xuất được tính toán một lần khi bắt đầu chương trình và được sử dụng lặp lại cho mỗi truy xuất đến bảng tam giác
1.3.3 Các bảng lồi lõm
Để tạo mảng truy xuất, ta phải xây dựng bảng lồi lõm theo thứ tự bắt đầu từ hàng đầu tiên Phần tử 0 của mảng truy xuất, là bắt đầu của mảng liên tục Sau khi mỗi hàng của bảng lồi lõm được xây dựng xong, chỉ số của vị trí đầu tiên chưa sử dụng tới của vùng nhớ liên tục chính là giá trị của phần tử kế tiếp trong mảng truy xuất và được sử dụng để bắt đầu xây dựng hàng kế của bảng lồi lõm
Bảng lồi lõm không có một quan hệ có thể đoán trước nào giữa vị trí của một hàng và chiều dài của nó Ví dụ như Hình 1.8 dưới đây
Trang 251.3.4 Các bảng chuyển đổi
Các bảng chuyển đổi là việc sử dụng nhiều mảng truy xuất để tham chiếu cùng lúc đến một bảng các phần tử qua một vài khóa khác nhau Với phương pháp này các thành phần dữ liệu được xem như là khóa đều được xử lý cùng một cách Các phần tử có thể lưu trữ theo một thứ tự tùy ý (thứ tự mà các phần tử được nhập
từ hệ thống) Các phần tử thuộc danh sách liên kết (các phần tử của các mảng truy xuất chứa các địa chỉ đến từng phần tử riêng)
Ví dụ như Hình 1.9
Trang 26Index Name Address Phone
2 Nguyễn Bảo Anh Cửa Lò, Nghệ An 2884285
3 Hồ Thị An Nghi Xuân, Hà Tĩnh 4372296
4 Nguyễn Thị Thủy Diễn Châu, Nghệ An 2863386
5 Nguyễn Thị Xuân Bỉm Sơn, Thanh Hóa 2495723
Hình 1.9 Mảng truy xuất cho nhiều khóa bảng chuyển đổi
1.4 Tìm kiếm theo phương pháp băm
Các phép toán trên các cấu trúc dữ liệu như danh sách, cây nhị phân,… phần lớn được thực hiện bằng cách so sánh các phần tử của cấu trúc, do 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 Phần tìm kiếm theo phương pháp băm([3], [4], [9])
này sẽ giúp hạn chế số lần so sánh, và vì vậy sẽ giảm thiểu được thời gian truy xuất thông tin Độ phức tạp của các phép toán trên bảng băm thường có bậc là O(1) và không phụ thuộc vào kích thước của bảng băm Bảng băm là một cấu trúc dữ liệu hiệu quả để cài đặt các từ điển, sử dụng hàm băm
Trang 27để ánh xạ từ giá trị xác định, được gọi là khóa Do đó, bảng băm là một mảng kết hợp Hàm băm được sử dụng chuyển đổi từ khóa thành chỉ số (giá trị băm) trong mảng lưu trữ các giá trị tìm kiếm
1.4.1 Bảng băm
Sau đây là dạng bảng băm cơ bản
Giả sử ta có một tập phần tử gồm các giá trị khoá bất kỳ được tổ chức lưu trữ dưới dạng bảng chỉ mục m phần tử như sau gọi là bảng truy xuất trực tiếp
- Phần tử có giá trị khoá k được lưu trữ tương ứng tại vị trí thứ k trong bảng chỉ mục
- Để tìm kiếm một phần tử nào đó ta sẽ dựa vào khoá của nó và tra trong bảng chỉ mục, nếu tại vị trí đó có phần tử thì chính là phần tử cần tìm, nếu không
có phần tử nào có nghĩa là phần tử cần tìm không có trong bảng chỉ mục
- Thời gian tìm kiếm là hằng số O(1)
Mô tả dữ liệu Bảng băm được mô tả bằng các thành phần sau:
- 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
Trang 28 Các phép toán trên bảng băm
Các phép toán trên bảng băm gồm: Khởi tạo, Kiểm tra rỗng, Lấy kích thước của bảng băm, Tìm kiếm, Thêm mới phần tử, Loại bỏ, Sao chép, Duyệt
Các bảng băm thông dụng
- Bảng băm với phương pháp nối kết trực tiếp: mỗi địa chỉ bảng băm tương ứng với một danh sách liên kết Các nút bị xung đột được nối kết với nhau trên một danh sách liên kết
- Bảng băm với phương pháp nối kết hợp nhất: Bảng băm loại này được cài đặt bằng danh sách kề, mỗi nút có hai trường: trường key chứa khóa của nút
và trường next chỉ nút kế bị xung đột Các nút bị xung đột được nối kết với nhau qua trường liên kết next
- Bảng băm với phương pháp dò tuyến tính: ví dụ như khi thêm nút vào bảng băm loại này nếu băm lần đầu bị xung đột thì lần lượt dò địa chỉ kế… cho đến khi gặp địa chỉ trống đầu tiên thì thêm nút vào địa chỉ này
1.4.2 Hàm băm
Hàm băm là hàm biến đổi khóa của nút thành địa chỉ trên bảng băm
Trong đó:
- Khóa có thể là khóa ở dạng số hay dạng chuỗi
- Địa chỉ được tính ra là các số nguyên trong khoảng 0 đến M – 1 với M là địa chỉ trên bảng băm
- Hàm băm thường được dùng ở dạng công thức:
Ví dụ như công thức f(key) = key%M với M là độ lớn của bảng băm
Khi hàm băm hai khoá vào cùng một địa chỉ thì gọi là đụng độ
Một hàm băm tốt thỏa mãn các điều kiện sau
- Tính toán nhanh
- Các khoá được phân bố đều trong bảng băm
- Ít xảy ra đụng độ
Trang 29Một số phương pháp xây dựng hàm băm
Hình 1.11 Hàm băm dạng bảng tra được tổ chức dưới dạng danh sách kề
Hàm băm sử dụng phương pháp chia
Hàm băm sử dụng phương pháp chia theo công thức dùng số dư:
h(k) = k mod m
Nghĩa là gắn k vào bảng có m ô nhờ sử dụng ô xác định bởi phần dư của phép chia
k cho m Trong đó k là khoá, m là kích thước của bảng
Trang 30Ta có tập khoá là các giá trị số gồm 3 chữ số và vùng nhớ cho bảng địa chỉ
có khoảng 100 mục, nhƣ vậy ta sẽ lấy hai số cuối của khoá để làm địa chỉ theo phép chia lấy dƣ cho 100: chẳng hạn 625 mod 100 = 25
Tuy nhiên ta nhận thấy nếu hàm băm dùng công thức nhƣ trên thì địa chỉ của khoá tính đƣợc chỉ căn cứ và hai ký tự cuối Vì thế, để hàm băm có thể tính địa chỉ khoá một cách ngẫu nhiên ta nên chọn m = 97 thay vì 100
M = 100 Khoá Địa chỉ
Trang 31Ví dụ 1.9
Ta có tập khoá là các giá trị số gồm 3 chữ số và vùng nhớ cho bảng địa chỉ
có khoảng 100 mục sử dụng trên hàm băm có hai giá trị A khác nhau: A = 0.52173,
A = 0.61803 Nhƣ vậy dựa vào công thức trên ta sẽ lấy đƣợc địa chỉ, chẳng hạn: 100*(625*0.52173 Mod 1) = 8
1.4.3 Bảng băm mở
Bảng băm mở là một số khóa có cùng địa chỉ, lúc này mỗi mục địa chỉ sẽ là một danh sách liên kết các phần tử có cùng địa chỉ, thời gian truy xuất có thể bị suy giảm đôi chút
Trang 32Bảng băm mở là phân chia tập hợp đã cho thành một số cố định các lớp Chẳng hạn, ta phân tập các phần tử cần quản lí thành N lớp, đánh số từ 0 N-1 Ta
sử dụng một mảng T với các chỉ số từ 0 đến N – 1, mỗi phần tử của mảng có thể lưu được các phần tử thuộc một lớp Các phần tử thuộc một lớp được tổ chức dưới dạng một danh sách liên kết Như vậy bảng T là bảng các con trỏ trong đó T[i] là con trỏ đến danh sách các phần tử thuộc lớp thứ i
Việc phân bố các phần tử của tập hợp A, với tập khóa K vào các lớp được thực hiện bởi một hàm h: K {0, 1, …, N-1}, gọi là hàm băm (hash function) Nếu
k là khóa một phần tử nào đó trong tập A, thì h(k) là chỉ số lưu phần tử này trong bảng T Ta gọi h(k) là giá trị băm của phần tử có khóa k Một trong các điều kiện bắt buộc của bảng băm lưu các phần tử là phải xây dựng được hàm băm hiệu quả
Có hai tiêu chuẩn chính để lựa chọn một hàm băm, trước hết phải đơn giản trong tính toán Thứ hai là phải phân bố đều các phần tử vào các lớp Trên thực tế tiêu chuẩn thứ hai khó đạt được Sau đây là một số phương pháp thiết kế hàm băm
a Phương pháp cắt bỏ
Giả sử khóa của các phần tử là những số nguyên Ta sẽ bỏ đi một phần nào
đó của khóa và lấy phần còn lại làm giá trị băm của khóa Chẳng hạn, khóa là các số nguyên 10 chữ số và bảng băm gồm 1000 thành phần, khi đó ta có thể lấy ba chữ số
Trang 33Ví dụ, với khóa k = 1234567890 được biến đổi thành 123 + 456 + 78 + 90 =
774 Với khóa 9876543210 được biến đổi thành 987 + 654 + 32 + 10 = 1683, sau khi cắt bỏ phần đầu còn lại giá trị băm là 683
Vì mọi thông tin trong khóa đều được phản ánh vào giá trị băm, nên phương pháp gấp cho phân bố các khóa đều hơn phương pháp cắt bỏ
c Phương pháp lấy phần dư
Với khóa là các số nguyên, giả sử cần chia tập hợp các phần tử thành M lớp
Ta lấy khóa chia cho M lấy phần dư làm giá trị băm Hàm băm được xác định h(k)
= k mod M Tính phân bố đều của các khóa phụ thuộc nhiều vào việc chọn số N Tốt nhất chọn N là các số nguyên tố
1.4.4 Bảng băm đóng
Khác với bảng băm mở, mỗi thành phần T[i] của bảng lưu giữ con trỏ tới danh sách các phần tử của tập hợp được đưa vào lớp thứ i (i = 0 N-1); trong bảng băm đóng, mỗi phần tử của tập hợp được lưu trữ trong chính các thành phần T[i] của mảng Do đó bảng băm đóng là một mảng các phần tử
Với cách tổ chức lưu trữ các phần tử như trên thì sẽ xảy ra trường hợp đụng
độ khi có hai phần tử e1, e2 có khóa tương ứng là k1 và k2 mà h(k1) = h(k2) =i, vì khi
đó hai phần tử e1, e2 đều lưu vị trí i trong mảng Để giải quyết đụng độ thường dùng phương pháp băm lại Phương pháp băm lại như sau: lần lượt xét các vị trí h(k1), h(k2), … cho tới khi tìm được một vị trí trống để đặt phần tử k vào đó Nếu không tìm được vị trí nào trống thì bảng đã đầy và không thể đưa vào bảng Trong đó hi(k) (i= 1, 2, …) là các giá trị băm lại lần thứ i, nó chỉ phụ thuộc vào khóa k Sau đây là một số phương pháp băm lại
a Băm lại tuyến tính
Các hàm hi(k) được xác định như sau:
hi(k) = (h(k) + i) mod N
Trang 34Với cách băm như trên, mảng lưu các phần tử được xem như một mảng vòng tròn Mỗi khi cần băm lại của khóa k sẽ xét các vị trí h(k) + 1, h(k) + 2, …
Chẳng hạn, N = 10 và các khóa a, b, c, d, e có các giá trị như sau:
Hạn chế của phương pháp băm lại tuyến tính là các giá trị khóa sẽ được xếp liền vào sau các giá trị khóa ban đầu đã đưa vào bảng mà không có đụng độ Do đó, càng ngày các giá trị khóa càng tụ lại thành các đoạn dài và giữa các đoạn lấp đầy là các khoảng trống Vì vậy, việc tìm một vị trí trống trong bảng để đưa giá trị mới vào, càng về sau càng chậm
b Băm lại bình phương
Băm lại bình phương là một cách tốt hơn băm lại tuyến tính, tránh được sự tập trung của các giá trị khóa cùng giá trị băm, hàm băm lại được xác định như sau:
hi(k) = (h(k)+i2) mod N
Cách băm lại này có một hạn chế là các giá trị băm lại không lấp đầy tất cả các chỉ số của mảng Do đó khi cần đưa vào bảng một giá trị mới, có thể không tìm được vị trí trống mặc dù trong bảng vẫn còn các vị trí trống
Trang 35c Các phép toán trên bảng băm đóng
Để kiểm tra một khóa x trong bảng băm đóng T ta phải duyệt lần lượt các vị trí h(k), h1(k), h2(k), … Giả sử các phần tử của bảng chưa bị xóa Khi đó hoặc tìm được một vị trí trong bảng chứa khóa k, hoặc tìm được một vị trí trống đầu tiên
ht(k) Trong trường hợp này kết luận rằng khóa k không có trong bảng Tuy nhiên,
sẽ phức tạp hơn nếu trong bảng đã thực hiện một số thao tác xóa Trong trường hợp này, nếu tìm được một vị trí trống đầu tiên ht(k) thì chưa thể kết luận được k không
có trong bảng vì có thể vị trí này lúc thêm phần tử khóa k đã có nhưng sau đó đã bị xóa Do đó hoàn toàn có thể k nằm trong các vị trí ht+1(k), ht+2(k),…
Để đảm bảo rằng khi tìm ra vị trí trống đầu tiên ht(k) mà chắc chắn bảng không chứa k, ta đưa vào bảng hai loại hằng deleted để gán cho vị trí của phần tử bị xóa và hằng empty dùng cho những vị trí trống thực sự Mỗi khi xóa một phần tử thì vị trí này được gán hằng deleted Khi cần thêm một phần tử mới vào bảng, có thể thêm vào vị trí đã xóa Với mỗi khóa k, các thao tác insert, delete, member trên bảng băm đóng đều thực hiện theo cách chung là giá trị băm h(k), nếu tại vị trí này không đúng yêu cầu thì thăm dò lần lượt tại các vị trí h1(k), h2(k), …
1.5 Phương pháp địa chỉ mở
Phương pháp địa chỉ mở ([3], [4], [9]) là phương pháp mà tất cả các phần tử đều được cất giữ vào bảng Do đó mỗi ô của bảng hoặc là chứa khóa hoặc là nil
Ý tưởng thuật toán
- Để thực hiện bổ sung, nếu ô tìm được là bận, ta sẽ tiến hành khảo sát lần lượt (hay còn gọi là dò thử) các ô của bảng cho đến khi tìm được ô rỗng để nạp khóa vào Thay vì tìm tuần tự theo thứ tự 0, 1, 2, …, m-1 (sẽ đòi hỏi thời gian Θ(n)), dãy các vị trí thử sẽ phụ thuộc vào khóa được bổ sung Để xác định các ô dò thử ta sẽ mở rộng định nghĩa hàm băm
- Khi tìm kiếm, ta sẽ tìm dọc theo dãy các phép dò thử giống như dãy dò thử khi thực hiện chèn phần tử vào bảng
*Nếu tìm được phần tử giống với khóa đã cho thì trả lại phần tử đó
Trang 36*Nếu tìm đƣợc con trỏ nil thì phần tử cần tìm không có trong bảng
Ta mở rộng định nghĩa hàm băm nhƣ sau:
j:= h(k,i)
if T[j]= nil Then T[j] := k Return j Else i := i+1 End
if i = m then
Error “hash table overflow”
Cài đặt thuật toán
int M;
struct hash_table{
int data;
Trang 38j:= h(k,i)
if T[j] = j Then return j
Chèn khóa k vào bảng thành công
Chèn khóa k vào bảng không thành công
Trang 39i := i+1 end
return -1;
Cài đặt thuật toán
int hash_search(struct hash_table T[], int k ) {
Trang 40Kết quả thử nghiệm
Trường hợp không tìm thấy :
Hình 1.13 Kết quả thực hiện chương trình trường hợp không tìm thấy khóa k
trong bảng