Giáo trình Cấu trúc dữ liệu và giải thuật: Phần 2 cung cấp cho sinh viên những kiến thức về đồ thị, tập hợp và bảng tìm kiếm, sắp xếp và tìm kiếm, một số phương pháp thiết kế thuật giải. Thông qua giáo trình này, có thể rèn luyện cho sinh viên cách áp dụng các cấu trúc dữ liệu đã học và tư duy thuật toán dễ có thể thiết kế và cài đặt một số chương trình bằng một ngôn ngữ bậc cao. Mời các bạn cùng tham khảo.
Trang 1Chuong 5
ĐỒ THỊ
Trong chương này ta sẽ trình bày khái niệm đồ thị Đó là khái niệm
tổng quát nhất trong những cấu trúc dữ liệu mà ta mô tả trong giáo trình này Các cấu trúc tuyến tính, cấu trúc phân cấp và cả tập hợp mà ta đã học trong những chương trước, đều có thể xem như những trường hợp riêng của
đồ thị Cấu trúc đề thị không chỉ được nghiên cứu và ứng dụng trong Toán
học, Tin học mà còn trong nhiều lĩnh vực khoa học công nghệ khác Một đồ
thị có thể xem là một cấu trúc dữ liệu mà mỗi thành phần dữ liệu của nó có
thể có quan hệ với một số tuỳ ý các thành phần đữ liệu trong cấu trúc dó
Tức là, mỗi thành phần đữ liệu trong cấu trúc đồ thị có thể có nhiều phần
tử “đứng trước” nó, và có nhiều phần tử “đứng sau” nó
5.1 Đồ thị và một số khái niệm cơ bản
5.1.1 Khái niệm đồ thị
Có hai loại dé thi, dé thị có hướng và đồ thị vô hướng Đồ thị có hướng
(directed graph hay digraph) G 1A mét cap (V E), trong dé V 1A mét tap hop hữu hạn các phần tử, mỗi phần tử của nó dược gọi là một đỉnh (vertex), còn
E là tập tập các cung (arc) có hướng, mỗi cung nối hai đỉnh của đổ thí một cách có thứ tự, tức là có phân biệt đỉnh đầu và dỉnh cuối Đỉnh cũng còn được gọi là nút (node), cung có hướng cũng còn được gọi là cạnh có hướng (directed edge) Tap Ð các cung có thể đặt tương ứng với một tập con của
tích Đề-các VxV, mỗi phần tử của nó là một cặp đỉnh có thứ tự (v, w) và được biểu thị hình họe bằng một mũi tên có đầu ở w và đuôi ở v Người ta hay gọi cung nối hai đỉnh v và w là cung (v, w) Đặc biệU, v và w có thể
trùng nhau, khi đó ta có cung (v, v) Hình 5.1.a thể hiện một đổ thị có hướng với bốn đỉnh và sáu cung
Đồ thị uô hướng là đồ thị mà trong đó mỗi cạnh (edge) JA mét cung néi hai đỉnh không kể đến thứ tự của hai đỉnh đó Nói cách khác, nếu (v w) là
cạnh của một đồ thị vô hướng thì (w, v) = (v, w) Hình 6.1.b thể hiện một đồ
thị vô hướng với bốn đỉnh và sấu cạnh Khi nói để thị mà không chỉ rõ có
Trang 2@—92
Hình 5.1 Đồ thị có hướng và đồ thị vô hướng
5.1.2 Một số khái niệm cơ bản khác
Đồ thị con của dé thi G = (V, E) là G'= (V, E’), trong d6 Vcr V, Pc E
và G' làm thành một đồ thị (tức là E V'xV9,
Đường đi (path) từ đỉnh v đến dỉnh w trong dé thi 1A day đỉnh vụ,
v„, , v„ sao cho đỉnh đầu tiên của dãy vị chính là v, đỉnh cuối cùng của dãy
chính là w, và giữa mỗi cặp đỉnh liên tiếp, theo đúng thứ tự của dãy đều có
một cung (cạnh), nghĩa là (vị, v;), (v;, vạ), , (V„ ¡, vạ) là các cung Đường đi này được gợi là đi qua các đỉnh vị, vạ, , vụ Độ dài của đường đi được tính bằng số cung trên đường đi, trong trường hợp này là n~-1 Một đỉnh v được xem là một đường đi đặc biệt có độ dài bằng 0 từ v đến v
Đường đi đơn là đường đi không tự cắt Nói cách khác, đường đi đơn
là đường di không chứa cặp đỉnh nào trùng nhau ngoại trừ hai đỉnh đầu và cuối của nó Chu frinh là đường đi có đúng một cặp đỉnh trùng nhau là đỉnh đầu và đỉnh cuối của đường đi đó, Một đề thị dược gọi là liên thông
(connected) néu véi hai dinh bat kì của nó luôn có một đường đi từ đỉnh này
tới đỉnh kia Mỗi dé thị con liên thông tối đại của một đồ thị được gợi là một thành phần liên thông của đỗ thị đó
Hình 5.2 Đồ thị vô hướng với hai thành phần liên thông
196
Trang 3Một đồ thị vô hướng liên thông, không chứa chu trình sẽ được gọi là
một cây tự do (free tree) Hình 5.2 chỉ ra ví dụ về một đồ thị vô hướng gồm hai thành phần liên thông mà mỗi thành phần liên thông là một cây tự do
Thuật ngữ cây tự do hàm ý rằng nếu ta xác định một đỉnh bất kì của nó làm gốc thì ta sẽ có một cây thông thường như đã trình bày ở chương trước 5.1.3 Đồ thị có trọng số
Trong nhiều trường hợp, người ta gán cho mỗi cạnh của đồ thị một, gia tri ma ta goi 1A chi phi (cost) hay giá của cạnh Khái niệm này xuất hiện khi nghiên cứu các bài toán thực tế Chang han, bài toán tim đường đi ngắn nhất giữa mỗi cặp thành phố trong một vùng, hay bài toán phải xây dựng mạng lưới giao thông giữa các thành phố trong một quốc gia sao cho tổng chỉ phí là nhỏ nhất Khi đó độ dài của một đường đi không được tính bằng số cạnh trên đường đi đó mà được tính bằng tổng chi phí của các cạnh trên đường đi Chú ý rằng ta nói về “độ dài” của đường đi ngay cả khi chi phí là một đại lượng khác, như thời gian chẳng hạn
5.2 Kiểu dữ liệu trừu tượng đồ thị
Ta sẽ trình bày một số thao tác cơ bản trên đề thị Những thao tác cơ
bản đó có thể cài đặt dưới dạng hàm hay thủ tục như trong bảng sau Trong những tình huống cụ thể, chẳng hạn với đồ thị vô hướng hay với đồ thị không có trọng số, cần có những thay đổi phù hợp
Thủ tục CREATE(G) Tạo một đồ thị rỗng G
Thủ tục INSERTNODE(G, v) Bổ sung một đỉnh mới v, V = VLÿ {v}
Thủ tục INSERTEDGE(G, u, v, w) Bổ sung thêm một cung mới (u, v) có trọng số
w vào đổ thị G E = E U tu, v)}
Thi tuc DELETENODE(G, v) Xoá đỉnh v và các cạnh liên quan đến nó ra
khỏi đồ thị Thủ tục DELETEEDGE(G, u, v) Xoá cạnh (u, v) khỏi đồ thị
Hàm FIRST (v) Trả ra chỉ số của đỉnh kể đầu tiên của v,
Hàm NEXT (y, i} Trả ra chỉ số của đỉnh kể (sau đỉnh có chỉ số i)
của v
Hàm VERTEX (v, i) Trả ra đỉnh với chỉ số ¡ trong các đỉnh kể của v
Trang 4INSERTEDGE(G, 2, 4); INSERTEDGE(, 4, 2); INSERTEDGE(G, 3, 4);
Ví dụ: Cần duyệt (đi qua) toàn bộ các đỉnh kể với đỉnh i, tai mỗi đỉnh
Hinh 5.3 Lap trén caéc dinh ké vdiv
5.3 Biểu diễn đồ thị
- Có nhiều cách để biểu diễn đổ thị trong máy tính, chẳng hạn biểu
diễn mối quan hệ đỉnh-canh hay biểu diễn mối quan hệ đỉnh-dỉnh Trong việc cài đặt một đồ thị, người ta thường lựa chọn mối quan hệ dỉnh-dỉnh
mà cụ thể là quan hệ kể cận hay láng giềng giữa các đỉnh Sau dây, ta sẽ trình bày hai cấu trúc dữ liệu biểu điễn mối quan hệ này Đó là ma trận các đỉnh kể và danh sách các đỉnh kể,
198
Trang 55.3.1 Ma trận các đỉnh kề
Một trong các phương pháp thông dụng biểu diễn đề thị là sử dụng
ma tran ké (adjacency matrix) Gia st (V, E) 1A mét dé thi V = (1, 2, 3, , n}
Ma trận kể biểu diễn G là ma trận vuông A cỡ n x n gồm các giá trị hai trạng
thai (0-1 hay true-false) được xác định như sau:
lnếu(v,w)eE néu(vw)eE nấu G là đồ thị không có trọng số „ Aly, wis!
Ta có thể cài đặt một số hàm như sau:
Type AdjType = arrayv[1 n,1 n] of boolean;
Vertex = integer;
var a: AdjType;
function FIRST(v: vertex): vertex ;
199
Trang 6Hinh 5.5 Cài đặt một số hàm trên đồ thị vô hướng bằng ma trận kế
5.3.2 Biểu diễn đồ thị bằng danh sách các đỉnh kể
Phương pháp thứ hai biểu diễn đồ thị là sử dụng danh sách các đỉnh kề
Ta xem mỗi đồ thị như một mảng các dang sách Danh sách thứ ¡ bao gém
các nút kể với nút ¡, với ¡ = 1, 2, , n Ta có thể cài đặt một số hàm như sau:
var Header = arraVy[1 n] o£ LIST;
function FIRST (v: vertex): vertex ;
begin p:= LOCATE(i, Header{ v]) ;
NEXT:= RETRIEVE (FIRST (Header v] )) end;
Hình 5.7, Cai đặt một số hàm trên đồ thị bằng danh sách liên kết các dinh ké
200
Trang 75.4 Duyệt đồ thị
Cũng như đã thực hiện đối với cây, trong mục này chúng ta xét bài toán đi qua các đỉnh của một đồ thị (còn gọi là duyệt hay tìm kiếm) Ta
sẽ trình bày trong mục này hai thuật toán duyệt đồ thị: ưu tiên độ sâu
(depth — first) vA uu tién bé réng (breath —first)
5.4.1 Tìm kiêm ưu tiên độ sâu (Depth First Searching)
Duyệt theo chiều sâu là đi qua tất cả các đỉnh trên một đường đi nào
đó của đồ thị cho đến khi không thể đi tiếp được nữa hoặc đường đi tạo
thành một chu trình (có đỉnh bị duyệt lại) Việc duyệt được thực hiện tiếp theo bằng cách quay lại đỉnh ngay phía trước và đi theo một đường đi khác
nếu thấy xuất hiện Cứ như vậy, cho đến khi các đỉnh của dé thị được đi qua hết
Về cấu trúc đữ liệu, để tiện cho việc trình bày, trong các thuật toán duyệt đổ thị dưới đây, ta sử dụng mảng order để ghi thứ tự các đỉnh đã được duyệt, với những đỉnh ¡ chưa được duyệt, ta quy ước order[1] = 0 Ngoài ra, ta cần có thủ tục visit thực hiện chức năng đi qua một đỉnh v, Thủ tục này sẽ tăng biến đếm count và gán cho order[v] giá trị của
count Điều đó hàm ý rằng count là thứ tự của đỉnh v trong phép duyệt
Việc duyệt đổ thị được mô tả như sau:
Tvpe Vertex = integer;
var count: integer;
i: Vertex;
order: array{1 n) of integer;
procedure visit(v: Vertex};
Trang 8end;
(b) Thủ tục đệ quy
for i:=1 to n do order i} :=0;
for i:=1 ton do
if order[ i} =0 then DFS (i)
END
(c) Lời gọi thủ tục đệ quy từ chương trình chính
Hình 5.9 Lược đồ đệ quy tìm kiếm ưu tiên độ sâu
Để có một thủ tục duyệt dé thị ưu tiên độ sâu không đệ quy, hãy chú
ý rằng ta đã có một thủ tục như vậy khi duyệt cây theo thứ tự trước với quy tắc DEPTH-FIRST-LEFT-MOST Khi áp dụng thuật toán đã xét vào đồ ' thi, cần chú ý rằng một đỉnh chỉ được duyệt khi nó chưa được duyệt một
lần nào trước đó (order[u]=0) Trong cây, điều kiện này dược kiểm soát thông qua quy tắc duyệt (trước, sau hay giữa)
Trang 9then begin
m:= NEXT(TOP(S));
POP(s)
end until EMPTY (s)
Hình 5.10 Lược đồ không đệ quy tìm kiếm ưu tiên độ sâu
Tuy nhiên, việc duyệt một đồ thị trả lại thông tin nhiều hơn một thứ
tự tuyến tính giữa các đỉnh Đó là một rừng với các cây được sắp thứ tự
Mỗi thành phần liên thông cho một cây của rừng Các cây này nếu được duyệt theo thứ tự trước sẽ cho lại các đỉnh theo thứ tự mà lần đầu tiên
chúng được gặp trong quá trình tìm kiếm Còn nếu được duyệt theo thứ tự sau, các cây này sẽ cho lại các đỉnh theo thứ tự mà quá trình duyệt đỉnh đó
hoàn tất
Hình 5.11 Rừng khung ưu tiên độ sâu của đồ thị được cho trong hình 5.8
203
Trang 10Giả sử đồ thị G có ø đỉnh và m cạnh Khi đó thời gian tìm kiếm ưu tiên độ sâu trên một đổ thị biểu điễn theo danh sách kể cố O(m+n) còn thời gian tìm kiếm ưu tiên độ sâu trên một đổ thị biểu diễn theo ma trận các
đỉnh ké 14 cd O(n’)
5.4.2 Tim kiếm ưu tiên bề rộng (Breath First Searching)
Duyệt theo chiều rộng có nghĩa là sau khi thăm đỉnh N, thì sẽ thăm các đỉnh kể với N Có thể hình dung một cách trực quan như sau Giả sử người ta đặt đổ thị vào một dãy những vòng tròn đồng tâm theo cách sau:
Đỉnh xuất phát của quá trình duyệt được đặt ở hình tròn trong cùng Các đỉnh kể với đỉnh xuất phát được đặt trong hình tròn thứ hai (kể từ trong) Các đỉnh kể với các đỉnh này (nếu chưa được đặt) sẽ được đặt trong
hình tròn tiếp theo, và cứ như thế cho đến hết các đỉnh Khi đó các đỉnh
sẽ được duyệt theo thứ tự từ trong ra ngoài của các hình tròn đồng tâm Dĩ
nhiên ở mỗi hình tròn các đỉnh được duyệt theo thứ tự của các đỉnh ở hình tròn bên trong mà nó là đỉnh kể
Trang 11end;
Hình 5.13 Thủ tục duyệt đồ thị trụ tiên bề rộng
5.4.3 Nhận xét
Trong cả hai thuật toán đã nêu, ở mỗi bước duyệt, các đỉnh được chia
thành ba lớp: các đỉnh đã được duyệt cùng với cạnh làm cho nó được duyệt làm nên cấu trúc cây, dược gọi là các đỉnh cây (tree vertice), các đỉnh nằm trong một danh sách chờ được duyệt được gọi là các đỉnh ven (fringe vertice) và các đỉnh chưa gặp lần nào trong quá trình duyệt được gọi là các đỉnh không nhìn thấy (unseen vertice)
Sự giống nhau của hai thuật toán là ở quy tắc:
s Chuyển một đỉnh (gọi nó là v) từ tập đỉnh ven vào tập đỉnh cây,
® Đặt tất cả những dỉnh không nhìn thấy, kể với v vào tập đình ven
Sự khác nhau của hai phương pháp duyệt là ở chỗ chúng quyết định đỉnh
nào được chuyển từ tập đỉnh ven vào tập đỉnh cây
Trong phép duyệt ưu tiên độ sâu, người ta chọn đỉnh cuối cùng được nạp vào danh sách đỉnh ven Điều này ứng với việc danh sách các đỉnh ven được lưu trữ bởi một ngăn xếp Trong phép duyệt ưu tiên bể rộng người ta
chọn đỉnh được nạp đầu tiên trong các đỉnh thuộc danh sách đỉnh ven, Điều
này ứng với việc danh sách các đỉnh ven được lưu trữ bởi một hàng đợi
Điểm khác nhau cơ bẵn nói trên dẫn đến sự khác nhau về trong việc cài đặt thuật toán Phép duyệt ưu tiên bể rộng có thể cài đặt đơn giản bằng
cách sử dụng một hàng đợi Trong khi đó, phép duyệt ưu tiên chiều sâu
được cài đặt đệ quy hoặc được khử đệ quy bằng cách sử dụng một ngăn xếp
Trang 125.5 Bài toán tìm đường đi ngắn nhất
8.5.1 Đường đi ngắn nhất từ một đỉnh
Trong mục này, ta xét bài toán tìm đường đi trên một đô thị có hướng Cho để thị có hướng G = (V, E), trong đó, mỗi cung được gắn với một giá trị mà ta gọi là chi phí (cost) của cùng Giả sử có một đỉnh đã cho được gọi là đỉnh nguồn Vấn đề đặt ra là tìm độ dài đường di ngắn nhất từ đỉnh
nguồn đến mỗi đỉnh còn lại trong V, trong đó độ dài của một đường đi chính là tổng chỉ phí của các cung trên đường đi đó Chú ý rằng ta nói về
“độ dài” của đường đi ngay cả khi chỉ phí là một đại lượng khác, như thời gian chẳng hạn,
Bài toán được giải quyết bằng kĩ thuật “tham lam” với một giải thuật được E.W Dijkstra phát triển vào khoảng 1959 và thường được gọi là giải thuật Dijkstra Giả sử đồ thị đã cho G = (V, E) có V = (1, 2, , n} và 1 là đỉnh nguồn Chi phi cha các cung đồ thị được cho bởi mảng hai chiều c,
trong đó c1, J] là chi phí gắn với cung (, j) Trong trường hợp không có cung
từ ¡ tời j thì ta đặt c[1, j] là ( mà thực chất là một giá trị lớn hơn các giá trị
có trong thực tế Thuật toán Dijkstra được mô tả trong hình sau
Hình 5.14 Thuật toán Dijkstra
Ví dụ: Cho đỗ thị có hướng như trong hình 1 Khi đó, ma trận chỉ phí
sẽ được mô tả trong hình 2 và quá trình thực hiện thuật toán Dijkstra sẽ
được thể hiện bằng bằng trong hình ä
206
Trang 13
Hình 5.17 Tính toán theo thuật toan Dijkstra với đồ thị trong hình 5.15
Để xác định được đường đi với độ dài ngắn nhất tìm được trong thuật
toán trên, người ta sử dụng một mảng P lưu trữ các đỉnh trung gian khi di
từ một đỉnh tới một đỉnh khác Ban đầu P[v] được gán bằng 1 (dỉnh xuất phát) với mọi v z 1 Cứ mỗi khi điều kiện d[u]+e{u,v] < dịv] được thoả mãn,
ngoài việc gán lại giá trị cho d[v] người ta lại gần P[v] bởi u Khi thuật toán
kết thúc, đường đi tới mỗi đỉnh được khôi phục bằng cách lần ngược từ dích nhờ vào mảng P
207
Trang 145.5.2 Đường đi ngắn nhất giữa mọi cặp đỉnh
Khi cần tìm dường di ngắn nhất giữa mọi cặp đỉnh của một đồ thị có
hướng, có trọng số, người ta thường dùng giải thuật Floyd Giải thuật này được mô tả như sau:
Procedure Floyd (var A: arrav[l n,1 n] o£ real;
C: array[l n,1 n) of real); var i,j,ka: integer;
if Al i,k] +A[ k,3] <Al i, 3]
then Af i,j] :=Al i,k] tAL k,j];
Trang 15Vi du: Trong vi du nay, ta xét bài toán tìm tâm đồ thị Cho một đỗ thị
có hướng có trọng số G = (V, E) Ta kí hiệu độ đài đường đi ngắn nhất từ đỉnh w đến đỉnh v là d(w,v) Độ lệch tâm của đỉnh v được định nghĩa là max d(w,v) Tâm của để thị là đỉnh có độ lệch tâm nhỏ nhất
(b) Bang gia trị độ lệch tâm
Hình 5.21 Độ lệch tâm của các đỉnh trong đổ thị có hướng cô trọng số
Hình 5.21.) liệt kê độ lệch tâm của các đỉnh thuộc đổ thị được cho ở
Hình 5.21.a Từ bằng giá trị này, thấy ngay rằng đỉnh d có độ lệch Lâm nhỏ nhất nên là tâm của để thị
Tìm tâm đổ thị là một ứng dụng của thuật toán Floyd Giả sử G là ma
trận chi phí của G Khi đó, thuật toán tìm tâm gồm ba bước sau:
1 Ap dung thủ tục Floyd được cho ở Hình 5.18 để tính ma trận A lưu
độ dài đường đi ngắn nhất giữa mọi cặp đỉnh của G
2 Tìm độ lệch tâm của mỗi đỉnh bằng cách tính giá trị lớn nhất của
từng cột
3 Tim đỉnh có độ lệch tâm nhỏ nhất Đó chính là tâm của G
Thời gian chạy của bước thứ nhất cỡ O(n?, bước thứ hai cd O(n”), và bước thứ ba cỡ O(n) Vì vậy thời gian chạy của giải thuật là O(n?)
Trang 16
max ¡ œ 6 8 5 7
Hình 5.22 Ma trận chỉ phí đường đi ngắn nhất giữa mọi cặp đỉnh
5.6 Cây khung với giá tối thiểu
Giả sử G = (V, E) là một đổ thị vô hướng liên thông, trong đó mỗi cạnh được gắn với một giá trị được gọi là chí phí hay giá của cạnh đó Cây khung của G là cây tự do nối tất cả các đỉnh trong V Giá của cây khung là
tổng giá của các cạnh trong cây Trong mục này ta sẽ bài toán tìm cây
khung với giá tối thiểu của G
Ví dụ: Hình 5.23 thể hiện một đổ thị vô hướng có trọng số và cây khung với giá tối thiểu của nó
Hình 5.23 Đồ thị vô hướng cô trọng số và cây khung có chỉ phí tối thiểu
Có hai cách thường được sử dụng để tìm cây khung với giá tối thiểu
Một trong hai cách đó là thuật toán Prim Giả sử V = {1, 2, n} Thuật toán Prim được bắt đầu với tập Ù = {1} và cây T rỗng Cây khung T sẽ lớn
dần lên theo cách mỗi lần thêm một cạnh Tại mỗi bước của thuật toán, sau khi tìm được tìm cạnh có giá nhỏ nhất trong các cạnh (u, v) với ueU va
veV \ U, người ta bổ sung đỉnh v vào U và bổ sung cạnh (u, v) vào T
Thuật toán Prim được phác thảo như sau:
210
14.CTDL-B
Trang 17Procedure Prim(G:GRAPH; var T:Tap các cạnh);
Hình 5.24 Các cạnh được bổ sung theo thuật toán Prim
Có thể thấy ngay rằng nếu G có n đỉnh thì thuật toán Prim thực hiện
n—1 phép lặp có độ phức tạp cỡ O(n) Vì vậy độ phức tạp của giải thuật
Prim là O(?) với n là số đỉnh của G
Trang 18Một cách khác để tìm cây khung với giá tối thiểu của G là sử dụng
thuật toán Kruskal Theo đó, cây T được khởi đầu bằng đô thị (V, Ø), tức là
đồ thị gồm tất cả các đỉnh của G nhưng không có một cạnh nào Khi đó, để thị gồm nhiều thành phần liên thông mà mỗi thành phần liên thông gồm
đúng một đỉnh Quá trình thức hiện thuật toán sẽ được thực hiện với từng
cạnh của đổ thị G đã cho theo thứ tự tăng dần của giá Nếu cạnh được xét
nối hai đỉnh của hai thành phần liên thông khác nhau của T thì ta bổ sung
nó vào T, còn nếu nó nối hai đỉnh của cùng một thành phần liên thông thì
bỏ qua (vì nếu bổ sung vào T, nó sẽ tạo thành chu trình) ;
Như một bài tập, bạn đọc hãy chỉ ra rằng độ phức tạp tính theo thời
gian của giải thuật K’ruskal 1A O(m.logm) véi m là số cạnh của đồ thị đã cho Như vậy, nếu m lớn hơn hay xấp xỉ n° thì thuật toán Prim là tốt bơn
thuật toán K”ruskal; còn nếu w nhỏ hơn n° nhiều thì thuật toán K'ruskal là
Trang 19Câu hỏi và bài tập
1 Hãy biểu diễn đổ thị có hướng trong hình 5.27,
a Bằng ma trận kể chỉ phí đã cho trên cung
b Bằng danh sách móc nối các đỉnh kể với chỉ phí đã cho trên cung
Hình 5.27 Đồ thị có hướng cô trọng số
9 Thực hiện phép duyệt đề thị được cho ở hình 5.27 theo phương pháp:
a Ưu tiên theo chiều sâu
b Ưu tiên theo bề rộng
8 Mô tả mô hình toán học cho bài toán xếp lịch sau Cho các nhiệm
vụ Tụ, T;, ,T„ tương ứng với thời gian cần thiết để hoàn thành mọi nhiệm vụ
4 Cài đặt các thao tác FIRST, NEXT và VERTEX cho đề thị có hướng được biểu diễn bởi:
a Ma trận kể
b Danh sách (liên kết) các đỉnh kể
5, Viết chương trình tìm đường đi đài nhất trong một đồ thị có hướng,
có trọng số, không chứa chu trình
6 Xét đồ thị có hướng được cho ở hình 5.27
a Hãy sử dụng giải thuật Dijkstra để tìm đường đi ngắn nhất từ a
đến các đỉnh khác
b Hãy sử dụng giải thuật Floyd để tìm khoảng cách ngắn nhất giữa mỗi cập đỉnh Đồng thời xây dựng ma trận P cho ta cái phủ của các đường đi ngắn nhất
213
Trang 207 Tim tâm của đồ thị được cho ở hình ð.27
8 (*) Chứng minh rằng chương trình Dijkstra hoạt động không dúng nếu các giá trên cung là âm
9 (*) Chứng minh rằng chương trình Floyd vẫn hoạt động đúng nếu
một số cung có giá âm nhưng không chu trình nào có giá âm cả
10 Xét đồ thị vô hướng nền của đồ thị cho trong hình 5.27 Giả sử giá
các cung của đồ thị có hướng được gán cho các cạnh của dé thi vô
hướng Tìm cây khung với giá tối thiểu của để thị này
214
Trang 21Chuong 6
TAP HOP VA BANG TIM KIEM
Chúng ta vừa nghiên cứu một số cấu trúc đữ liệu tuyến tính và một
số cấu trúc dữ liệu phân cấp Trong chương này và chương tiếp theo ta sẽ nghiên cứu một số cấu trúc đữ liệu khác, đó là tập hợp (set) và bảng tìm kiếm (search table)
6.1 Tập hợp
6.1.1 Định nghĩa và các thao tác
Ta đã biết trong toán học về khái niệm tập hợp Tập hợp được hiểu là một họ các phần tử có những tính chất chung nào đó Ta đã biết các tập hợp quen thuộc như tập hợp số tự nhiên N, tập hợp số nguyên Z, tập hợp số
thực R Một cách tổng quát, ta không có quy ước gì ràng buộc về các phần
tử của một tập hợp Chẳng hạn, ta có thể có tập hợp như sau:
ước đó như sau:
1, Các phần tử của một tập hợp phải thuộc cùng một kiểu và được gọi
là kiểu cơ sở
2 Kiểu cơ sở phải là kiểu có thứ tự Kiểu cơ sở thường gặp nhất là kiểu số nguyên, kiểu kí tự, hay một kiểu có thứ tự do người sử dụng định nghĩa Không có khái niệm tập hợp những mảng hay tập hợp bản ghi
3 Số các phần tử của một tập hợp bị giới hạn
Như vậy, khái niệm tập hợp trong phạm vi của chúng ta tương đối
khác so với khái niệm tập hợp nói chung trong toán học Với quy ước của chúng ta thì tập hợp đưa ra trong ví dụ ở phần trên là không thỏa mãn vì
Trang 22các phần tử của nó không thuộc cùng một kiểu Tập hợp U = {1, 3, 7, 9} là
hoàn toàn thỏa mãn các quy ước trên, Thứ tự của các phần tử trong một tập hợp là không quan trọng Tập hợp U có thể viết là U = {3,-7, 1, 9} Tính không có thứ tự của các phần tử trong một tập hợp là tính chất đặc trưng
làm cho nó khác hẳn với các cấu trúc dữ liệu tuyến tính và phân cấp Với
cấu trúc đữ liệu tuyến tính chúng ta thường có khái niệm phần tử đầu tiên, phần tử cuối cùng, phần tử liển trước, phần tử liền sau Những khái niệm
ấy hoàn toàn không có trong tập hợp Trong cấu trúc đữ liệu phân cấp, ta cũng có khái niệm cha, con, gốc, lá Những khái niệm này cũng không hé
có trong tập hợp Các phần tử trong một tập hợp không có quan hệ gì với nhau trừ việc chúng đều là thành viên của tập hợp đó Những thao tác trên tập hợp là những thao tác quen thuộc và chúng ta cũng không khó khăn lắm để đặc tả môđun ngoài cho kiểu dữ liệu trừu tượng tập bợp
Thao tác tạo tập hợp được gọi là CreateSets:
CreateSetQ : Set
nó tạo ra một tập hợp không chứa phần tử nào và được gọi là tập hợp rỗng
Trong lớp thao tác có tính chất biến đối gồm có năm thao tác chính
Thao tác thứ nhất là thao tác chèn một phần tử E vào tập hợp S để có một tập hợp mới Nếu phần tử E đã thuộc 8 thì S không có gì thay đổi sau thao
tác chèn vì trong một tập hợp không cho phép có hai phần tử trùng nhau
Thao tác thứ hai là thao tác xóa một phần tử E khỏi tập hợp 8 Nếu E không phải là một phần tử của 8 thì tap S sẽ không thay đổi Thao tác này cũng được xem như là một lỗi về cú pháp
Insert(Set, Element) : Set (* thêm một phần tử vào một tập hợp*)
Delete(Set, Element) : Set # Xoá một phần tử khối một tập hgp *)
Ba thao tác biến đổi còn lại liên quan đến việc tạo ra tập hợp mới từ hai tập hợp ban đầu Thao tác thứ nhất là phép lấy hợp (Union) của hai tập hợp S1 và 82 để có tập hợp 83 = S1 L¿ S2 mà các phần tử của nó bao hàm cả
những phần tử của Š1 và của 82 (đương nhiên là không có phần tử trùng
nhau) Thao tac lay giao (Intersection) cha hai tap hdp S1 va S2 dé có tập
hợp 83 = S1 ¬ 52 gồm những phần tử thuộc cả hai tập hợp S1 và §2 Cuối
216
Trang 23cùng là thao tác lấy hiệu (Differenee) của hai tập hợp S1 và S9 để có tập
hợp 53 = §1 — 82 gồm những phần tử thuộc 81 nhưng không thuộc S9 Ta
sẽ minh họa những thao tác này bằng phép sử dụng biểu đồ Venn dưới đây
Phần tô đậm của các hình tròn biểu diễn các phần tử được chứa trong tập
hợp mới S3 Cú pháp của các thao tác này là:
Union(Set, Set) : Set
Intersection(Set,Set) : Set
Difference(Set,Set) : Set
{A
Hinh 6.1 Biéu dé Venn vé ba thao tac co ban trên tập hợp
Có ba thao tác thuộc lớp thao tác mang tính chất quan sát, đó là thao tác Empty kiểm tra xem một tập hợp có phải là rỗng không, thao tác Member kiểm tra xem một phần tử có thuộc một tập hợp không và thao tác Subset kiểm tra xem một tập hợp có phải là tập hợp con của một tập hợp
khác không Cú pháp của chúng lần lượt như sau:
Empty (Set) ': Boolean
Member(Set, Element) : Boolean
Subset(Set, Set) : Boolean
Thao tac Empty cho két qua 1a true néu tập hợp không chứa phần tử
nào, cho giá trị là False trong trường hợp ngược lại Thao tác Member cho
giá trị là True nếu phần tử đó thuộc tập hợp và False trong trường hợp ngược lại Thao tác Subset sẽ cho kết quả là True nếu mợi phan tử của tập hợp thứ nhất đều là phần tử của tập hợp thứ hai, ngược lại nó cho kết quả
là False
Ví dụ:
Giả sử 8 = {1, 3, 5, 7};T = {3, 4, 5}; U = (8, 4}; V = Ø (tập rỗng)
Trang 24Đặc tả hoàn chỉnh cú pháp và ngữ nghĩa của các thao tác trên tập
hợp được cho trong hình sau:
Cài đặt
Có rất nhiều cách cài đặt tập hợp trong các ngôn ngữ lập trình bậc
cao Ví dụ chúng ta có thể lưu trữ các phần tử của tập hợp trong một mắng: Const
Trang 25Cú phúp:
Define Set[Element]
a CreateSet() : Set
b Insert(Set, Element) : Set
c Delete(Set, Element) : Set
d Union(Set, Set) : Set
e Intersection(Set, Set) : Set
f Difference(Set, Set) : Set
g Empty(Set) : Boolean
h Member(Set, Element) : Boolean
j Subset(Set, Set): Boolean
Ngữ nghĩa:
a Empty (CtreateQ) = True
b Member (Insert(S,E), E) = True
c Empty (nsert (S,E)) = False
d Member (Delete(S,E), E) = False
e Empty (Delete(Insert (Createset(), E), E) = True
f If Member (81, E) or Member (82, E) Then
Member (Union(S1, 82), E) = True
Else
Member (Union(S1, 82), E) = False
g If Member (S1, E) And (Member (82, E)) Then
Member (Intersection(S1, $2), E) = True
Else
Member (Intersection(S1, $2), E) = False
h If Member ($1, E) And (not(Member (52, E)) Then
Member (Difference(S1,82), E) = True
Else
Member (Difference(S1,S2), E) = False
i If (Empty(Difference(S1, 52))) Then
Trang 26Else
Subset (S1, $2) = False
Hình 6.2 Cú pháp và ngữ nghĩa hình thức của kiểu tập hop
Trong đó Max được giả định là số phần tử tối đa cho phép trong một
tập hợp còn kiểu cơ sở (BaseType) chính là kiểu của các phần tử trong tập
hợp Với những khai báo như thế thì tập hợp S = { 25, 13, 10, —4, 22} có thể được lưu trữ như sau:
Ta để ý rằng thứ tự được lưu trữ trong máy không nhất thiết trùng
với thứ tự ta liệt kê các phần tử trong một tập hợp
Sau đây ta khai báo môđun ngoài và môđun trong của kiểu dữ liệu trừu tượng tập hợp
(* Môdun sau đây chứa đặc tả bên ngoài của kiểu đữ liệu trừu tượng tập hợp như ta đã mô tả trong hình 5.1 Cú pháp và ngữ nghĩa của kiểu này đã dược mô tả trong hình ð.2*),
External Module SetPackage;
From UserModule Import
Trang 27Postcondition: Sau khi gọi thủ tục Insert, chắc chốn tập hợp sẽ chúa
phan tu vita thém
Member(Insert(S, E),E) = True
Empty(Insert(S, E), E) = False _*#)
Procedure Insert (Var Set : SefType; Element : DataBlementType j; (* PrecondiHon: Không
Postcondition: Sau khi gọi thủ tục Delete, chắc chắn tập hợp không
còn chứa phần tử đó nữa
Member(Delete(S, E),E) = False
Empty(Delete(Insert(Create(), E), E)) = True*)
Procedure Delete (Var Set : SetType; Element : DataElementType ); (* Precondition: Không
Postcondition: Ham hay thu tuc Union tra ra mét idp hop gém nhiing phân tử hoặc thuộc S1 hoặc thuộc S2 hoặc thuộc cả hai tap hop
If Member(S1, E) Or Member(S2, E) Then
Postcondition: Ham hay thi: tuc Difference trả ra một tập hợp gồm
những phần tử thuộc tập hợp S1 nhưng không thuộc S82
Trang 28If Member(S1, E) And (not(Member(S2, E)) Then
Postcondition: Thit tue Member trd ra giá trị True nếu phần tử đó
thuộc tập hợp, trả ra giá trị False nếu ngược lại *)
Procedure Member( Set: SetType; Element: DataElementType ):
hạn, ta có thể tìm kiếm tuần tự trên mảng xem một phần tử đã cho có
thuộc tập hợp hay không Hình 5.4 cho ta thấy khi dùng mảng cài đặt tập hợp, thao tác lấy giao S3 của hai tập hợp S1 và S2 được thực hiện như thế
nào Với mỗi phần tử của tập hợp S1 ta lần lượt duyệt S2 xem nó có thuộc
S2 không Nếu nó cũng thuộc 82 ta sẽ đặt nó vào tập hợp giao S3 Nếu nó 222
Trang 29không thuộc 52 thì ta bỏ qua nó Nếu hai mang S1 và 82 chưa được sắp xếp
và mỗi mảng đều có n phần tử thì độ phức tạp về thời gian của thao tác này
sẽ là O(n”) Độ phức tạp về thời gian sẽ tốt hơn nếu S1 và S2 da được sắp xếp Sau đây là thao tác lấy giao của hai tập hợp nếu ta dùng mảng để cài đặt tập hợp
(* Precondition: Khéng
Postcondition: Intersection tra ra tép hop gém những phần tử thuộc
cả hai tập hợp Set1 va Set2*)
Procedure Intersection( Set1 : SetType; Set2: setType): SetType; Var
Hinh 6.4, Dung mang cài đặt thao tác giao của hai tập hợp mảng
Nếu chúng ta dùng danh sách móc nối để cài đặt tập hợp ta cũng có kết quả tương tự như dùng mảng
Trong mục trước, chúng ta đã quy ước 3 điểu về kiểu cơ sở của tập hợp Cụ thể là ta quy ước tất cả các phần tử của tập hợp phải thuộc cùng
' một kiểu, và đó phải là kiểu đơn chứ không phải kiểi phức hợp Hơn thế
nữa, số phần tử của tập hợp không phải là tùy ý mà phải tương đối nhỏ Nếu chúng ta có thêm một quy ước nữa cho kiểu cơ sở ta sẽ có một cách cài đặt tập hợp rất hiệu quả và được gọi là biểu diễn bằng vectd bit
Điều mà chúng ta cần giả sử thêm là tổn tại f là một ánh xa 1-1 tt tập hợp tất cả cdc phan tt cla kiéu cd sé dén tap hop các số nguyên từ 0
Trang 30đến N—1 mà ta kí hiệu là [0 N-1], trong đó, N là số các phần tử của kiểu cơ
Tất cả các kiểu có thứ tự của Pascal như kiểu Integer, kiéu Chareter,
Boolean hay kiểu liệt kê đều có tính chất trên Trong những trường hợp này, hàm f mà ta nói đến ở trên có thể chọn chính là hàm chuẩn Ord(x),
Trong trường bợp hàm f như trên tốn tại thì để biểu diễn một tập hợp
Š có không quá N phần tử ta chỉ đơn giản tạo một mảng với N phần tử,
mỗi phần tử của mắng nhận một trong hai giá trị là Có và Không hay Yes
và No Yes tương ứng với việc phần tử có xuất hiện trong tập hợp 8, No sẽ tương ứng với việc phần tử đó không có trong tập hợp 8 Nói cụ thể hơn,
mỗi tập hợp 8 bây giờ tương ứng với một mảng N phần tử, mỗi phần tử có
một trong hai trạng thái là Yes hoặc No Việc khai báo thiết lập vectơ bịt
thành Yes Để xoá phần tử e khỏi tập hợp 8 ta chỉ cần gán giá trị No cho
S[f(e)], tức là làm cho trạng thái của phần tử đó trở thành No Để kiểm tra
xem e có phải là phần tử của S hay không chỉ đơn giản là kiểm tra vị trí
S[f(@)] xem giá trị tại đó là Yes hay No Ta thấy ngay với cách biểu diễn
như vậy, nhiều thao tác có độ phức tạp là O(1)
Để làm ví du, gia sử bây giờ chúng ta phải tạo ra một tập hợp biểu diễn các ngày trong một tuần: Thứ hai (Monday), Thứ ba (Tuesday), Thứ tư
224
Trang 31(Wednesday), Tha nam (Thursday), Tha sau (Friday), Thi bay (Saturday)
và Chủ nhật (Sunday) Hàm thứ tự Ord sẽ ánh xạ như sau:
Monday Tuesday Wednesday Thursday Friday Saturday Sunday
Để xác định xem phần tử Tuesday có thuộc tập hợp không ta chỉ việc kiém tra gia tri cha S[Ord(Tuesday)} = S[1] Ta thấy nó có giá trị là No Điều đó chứng tỏ rằng Tuesday không thuộc tập S
Để xóa phần tử Friday khdi tap hợp ta chỉ việc gán giá trị của S{Ord(Friday)] = S[4] bang No
Ta xét ví dụ thứ hai Giá sử tập hợp cơ sở là tập các chữ cái thường
trong bảng chữ cái ['a' Z], nó có thứ tự lần lượt từ 97 tới 122 Hàm f mà ta chọn bây giờ là:
Trang 32For i:= 0 To N-1 Do
If (S1[i] = Yes) And (S2[i]) = Yes) Then
S3[i} := Yes Else
S3[i] := No
Cách cài đặt tập hợp này giúp cho phép lấy giao hai tập hợp hiệu quả
hơn nhiều so với việc ta dùng mang hay danh sách móc nối để cài đặt Vì
nếu dùng máng hay danh sách móc nối, ta sẽ cần tới O(N?) phép so sánh Cũng tương tự như vậy cho phép lấy hợp hay hiệu của hai tập hợp
Sau đây ta trình bày môđun cài đặt (Internal Module) kiểu đữ liệu trừu tượng tập hợp khi dùng vectd bịt
(* Môđun sau đây là phần chỉ tiết bên trong của hiểu dữ liệu trừu
tượng Tập hop Set sw dung vecto bit Médun ngoài ta đã uiết ở hình 6.3%)
Internal Module SetPackage;
From UserModule Import
(* biểu dữ liệu của các phần tử của tập hợp *)
(* Kiểu dữ liệu trừu tượng *)
SetType = Pointer To Array [0 MaxElement — 1] of (Yes, No);
Trang 33NewSet : SetType;
i : Integer;
Begin
New(NewSet);
For i:= 0 to (MaxElement ~ 1) Do
NewSet^[il := No; ®% Chưa có phần tử nào thuộc tập hợp”)
Trang 34Postcondition: Intersection trả ra tập hợp gồm những phần tử thuộc
cả hai tập hợp Set1 và Set2*)
Procedure Intersection( Set1 : SetType; Set2: setType): SetType;
If (Set1*{i] = Yes) And (Set2“[i] = Yes)) Then
NewSet* [i] := Yes;
Return NewSet;
End Intersection;
(* Precondition: Khéng
Postcondition: Difference tra ra lập hợp gồm những phần từ thuộc
tập Set1 nhưng không thuộc thập Set2 *)
Procedure Difference( Set1 : SetType; Set2: setType): SetType;
Trang 35Postcondition: Empty tra ra gid tri True néu Set khéng chita phén
tử nào, ngược lại, nó trẻ ra giá trị False*)
Procedure Empty( Set : SetType): Boolean;
Var
1 : Integer;
Begin
While (i < MaxElement) Do (*duyét méi phần tử thuộc kiểu cơ sở*)
If (Set“[i] = Yes) Then (*dén khi tim duge mét phần tử thuộc Set *)
Return False; (* tap hdp Set sẽ khác rỗng *) 1:=it),;
Endwhile;
Return True;
End Empty;
Œ® Precondition: Không
Postcondition: Member tra ra gid tri True néu Element
là một phần tử thuộc Set, ngược lại, nó trả ra giá trị False*)
Procedure Member( Set : SetType; Element: DataElementType): Boolean;
Begin
Return Set*[Ord(Element)];
End Member;
(* Precondition: khéng
Trang 36Postcondition: Subset tra ra giá trị True nếu tập hợp thử nhấi SeL1
la tập hợp con của tập hop thit hai Set2, nguec lai n6 tra ra gid tri
Hình 6.5 Dung vecto bit cai dat kiểu dữ liệu trừu tượng tập hợp
Ở mức trừu tượng cao nhất, ta được phép xem tập hợp là một kiểu diz liệu trừu tượng mà không cần phải để ý đến việc kiểu đữ liệu trừu tượng được cài đặt thế nào trong ngôn ngữ lập trình Ta có thể xem nó như một phần của ngôn ngữ lập trình, chẳng hạn ta có thể viết:
Var
S : Set;(* Kiểu đữ liệu Set và Element được nhập từ môdun *)
E: Element; (* ngoài của chương trình *)
Chúng ta sử dụng những đối tượng của kiểu dữ liệu trừu tượng tập hợp này qua các thao tác được xây dựng trong môdun ngoài (hình 6.3) mà
chẳng cần quan tâm đến việc các thao tác này được cài đặt như thế nào Chẳng hạn, ta có thể viết:
From SctModule Import
Trang 37Theo quan điểm của người sử dụng, họ chỉ cần biết là họ có thể dùng được những gì, dùng chúng ra sao (cú pháp) và hiệu ứng (ngữ nghĩa) của chúng như thế nào
Ở mức tiếp theo, sử dụng các cấu trúc dữ liệu cho bởi một ngôn ngữ lập trình, chúng ta cần phải quan tâm đến kiểu đữ liệu trừu tượng được cài đặt ra sao Chẳng hạn như việc cài đặt tập hợp, ta có thể dùng mắng, danh sách móc nối hay vectơ bit Do khuôn khổ của giáo trình ta chỉ thực
hiện chỉ tiết việc cài đặt bằng vectơ bịt Bây giờ giả sử trong một chương trình, bạn cài đặt tập hợp như là mảng Ñ phần tử và sử dụng nó ngay
trong chương trình đó Chắc chắn các công việc đơn giản như kiểm tra xem một phần tử có thuộc một tập hợp hay không cũng có nhiều việc hơn rất nhiều so với việc bạn sử dụng kiểu dữ liệu trừu tượng tập hợp đã xây
dựng ở mức trên
Tóm lại, việc bạn quan niệm tập hợp như trong toán học hay là một mảng, một danh sách, một vectơ bịt là tùy theo nhu cầu của bạn và tùy theo bạn là một người nghiên cứu về tập hợp, người sử dụng, người lập trình hay người viết chương trình địch Tuy nhiên, nếu bạn là người sử
dụng hay người lập trình thì không nên bận tâm nhiều đến những chỉ tiết
ở mức dưới hoặc để chúng làm cần trở đến công việc của bạn
6.2 Bảng tìm kiếm
6.2.1 Định nghĩa và các thao tác
Tập hợp là một khái niệm quan trọng trong toán học và nó được
nghiên cứu rất nhiều trong một chuyên ngành hẹp gọi là lí thuyết tập hợp
Tuy nhiên, cấu trúc tập hợp mà ta vừa nghiên cứu ở mục trước không được đánh giá quan trọng lắm và cũng không được dùng nhiều trong khoa học
máy tính Những hạn chế đó có thể là do ta đã giới hạn kiểu cơ sở của tập hợp là những kiểu có thứ tự và giới hạn về số phần tử của tập hợp
Tuy nhiên, điểu đó không có nghĩa là tập hợp không đóng vai trò
quan trọng trong việc xây dựng phần mềm Với một số thay đổi trong định nghĩa tập hợp ở mục 6.1 ta có thể xây dựng được một khái niệm hết sức
quan trọng và có nhiều ứng dụng trong khoa học máy tính, đó là khái niệm
bang tìm kiém (Search Table) Bang nay bao gém những thao tác thông dụng trên một tập hợp những phần tử Sau đây là một số những thay đổi
Trang 381 Thay vì giới hạn các phần tử của tập hợp là kiểu có thứ tự, ta gid
sử rằng chúng bao gồm những bộ hai phần tử dạng (ki, vi) trong đó, thành phần thứ nhất được gọi là trường khóa còn thành phần thứ hai được gọi là trường giá trị Chúng ta không có hạn chế gì nữa trên số lượng các bộ trên nhưng đương nhiên là chúng phải hữu hạn
S = (Œ&0,v0), (k1,v1), kn,vn)}
các phần tử ki thuộc kiểu đữ liệu mà ta gọi là KeyType còn những phần
tử vi thuộc kiểu đữ liệu mà ta gọi là ValueType Những kiểu này không nhất thiết phải là những kiểu đơn Phần tử ki là duy nhất xác dinh trong
tập hợp với một tập 5 nhưng phần tử vi thì có thể không cần thiết phải
duy nhất
2 Chúng ta cũng không quan tâm đến các thao tác lấy hợp Union giao Intersection hay hiệu Difference của hai tập hợp nữa Bây giờ ta giới hạn là chỉ quan tâm đến các thao tác tạo lập (Create), chèn (InserÐ), xoá (Delete) và xem một phần tử nào dó có thuộc tập hợp đã cho không (Member)
8 Cú pháp của các thao tác trên một bang tìm kiếm sẽ bị thay đổi chút ít phần ánh sự khác nhau về cấu trúc của các phần tử của bảng Cú
pháp này được cho ở hình sau đây
Thao tac nay tạo ra một bảng tìm kiếm mới T9 chứa tất cả các phần tử
của T1 và còn chứa cả bộ (K,V) nếu như (K,V) không thuộc T1 Nếu như TI chứa một bộ phần tử dạng (K,X), trong đó X là giá trị bất kì thì thay vì thêm
giá trị (K,V) vào T2, ta thay giá trị X trong bộ đã tồn tại đó bằng giá trị V
3 Delete(T1,K) : T2
Thao tác này tìm trong T1 bộ phần tử dạng (K,X), trong đó X là giá trị bất kì, và nếu tìm được thì chuyển phần tử đó khỏi T1 để có bảng mới 232
Trang 39T2 Nếu không tìm thấy bộ phần tử như thế thì nó trả ra bảng cũ mà không hề thay đổi gì
4, Member(T,K) : V
Thao tác này tìm trong T bộ phần tt dang (K.V), va néu tim được thì
trả ra giá trị V liên kết với khóa K Nếu không tìm thấy K, nó trả ra một
gia tri dac biét trong V, goi 1A Null
Hình 6.6 Cú pháp và ngữ nghĩa của cấu trúc bảng tìm kiếm
Nội dung mà chúng ta vừa mô tả trong hình 6.6 là một ví dụ về một
cấu trúc đữ liệu truy cập trực tiếp Trong cấu trúc này, tất cả mọi giá trị dữ liệu (data value) V trong cấu trúc tại thời điểm chèn đều liên kết với một
giá trị duy nhất được gọi là khóa K và được lưu trữ dưới dạng bộ (Ñ, V) Mọi sự truy cập sau đó tới giá trị đữ liệu này đều thông qua khóa của nó, chứ không phải bởi vị trí của nó trong bằng như chúng ta đã biết trong mảng Do giá trị khóa được lưu trữ trực tiếp trong bộ nên thứ tự của các bộ trong bang là không quan trọng theo nghĩa xác định thuộc tính thành viên
(membership) Hơn thế nữa, do tính duy nhất của giá trị khóa, nên mỗi bộ
cũng được xác định duy nhất trong bảng, tức là không có hai bộ trùng nhau
trong một bảng Hai đặc tính này cùng làm cho cấu trúc bảng tìm kiếm mà
ta vừa mô tả thỏa mãn định nghĩa của tập hợp
Bảng tìm kiếm là một cấu trúc dữ liệu quan trọng và được sử dụng rất rộng rãi Việc truy cập thông qua khóa cho ta một mô hình gặp ở rất nhiều tình huống trong thực tế Sau đây là một số ví dụ:
Trang 40
Số thẻ sinh viên (Tên, Chuyên ngành học, Năm nhập học)
Số thẻ bảo hiểm (Tên, Địa chỉ, Loại bảo hiểm)
Số biển kiểm soát (Loại xe, Nơi đăng kí, Màu)
Trong tất cả các ví dụ trên, ta thường muốn biết giá trị dữ liệu khi
biết giá trị khóa (hay ta cần biết không tổn tại bộ có khóa đó) Thao tác này
chính là thao tác xác định thành viên Member trong hình 6.6
Médun ngoài của kiểu đữ liệu bằng tìm kiếm được mô tả trong hình 6.7 sau đây Thao tác xác định thành viên Member được đối tên thành thao
tác lấy thông tin Retrieve cho rõ nghĩa hơn Trong mục sau ta sẽ nghiên cứu xem làm thế nào để cài đặt cấu trúc này cho hiệu quả và chúng ta sẽ
đưa ra một số ví dụ về sử dụng chúng
6.2.2 Dùng mảng để cài đặt bảng tìm kiếm
Cũng như cấu trúc tập hợp trong phần 5.1, có nhiều cách đơn giản để cài đặt bảng tìm kiếm Tuy nhiên, ta sẽ thấy nói chung chúng không hiệu
quả và nhiều khi khó có thể chấp nhận được
(* Môdun sau đây chứa đặc tả ngoài cho hiểu dữ liệu trừu tượng bảng tìm hiếm như đã mô tả trong hình ð.6 *)
External Module SearchTablePackage;
From UserModule Import
Postcondition: Insert thém b6 (K, Value) vao bang Table, sao cho bat
kì bộ nào đã có trong Table dạng (K, Val) đều được thay thế *)
234