Tải bản đầy đủ (.doc) (10 trang)

Các phương pháp tìm kiếm cơ bản

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (200.3 KB, 10 trang )

Bài toán tìm kiếm và phương pháp tìm kiếm cơ bản
a. Bài toán: Tìm kiếm trên cây nhị phân là một thuật toán đơn giản, một phương pháp tìm kiếm động hiệu quả.
Phương pháp này là một trong các thuật toán nền móng của khoa học máy tính. Sở dĩ thuật tóan này được bàn ở
đây và được coi là cơ bản bởi lẽ nó đơn giản; nhưng lại là phương pháp tìm kiếm được chọn lựa trong nhiều
trường hợp ứng dụng.
Như chúng ta đã biết trong một cây: mỗi nút chỉ được trỏ tới bởi duy nhất một nút khác gọi là
cha của nó. Một cây nhị phân thì mỗi nút có 2 liền kề trái và phải. Với việc tìm kiếm, mỗi nút
của cây có một mẩu tin chứa giá trị khóa. Không mất tính tổng quát, ta giả thiết rằng: trong cây
– tìm – kiếm – nhị - phân tất cả các mẩu tin với các khóa nhỏ hơn thì ở trong cây – con – trái vàtất cả các mẩu
tin trong cây con phải có giá trị khóa lớn hơn hay bằng nhau. Chúng tathấy rằng sẽ hoàn toàn đơn giản để đảm
bảo cho cây tìm kiếm nhị phân thỏa mãn định nghĩa của nó khi chèn thêmvào một cây nút mới.
Thủ tục tìm kiếm giống như thủ tục tìmkiếmnhiphân ta đã xét ở phần trước. Tất nhiên, chúng ta sẽ luôn bám sát
với định nghĩa của cây nhị phân
b. Hướng giải quyết
Để tìm một mẩu tin với khóa k đã cho, trước tiên ta so sánh nó với nút gốc nếu nó nhỏ hơn thì đi đến cây con
trái. Nếu bằng thì dừng, nếu nó lớn hơn thì đi đến cây con phải.
Áp dụng đệ quy quá trình trên cho các cây con. Trong mỗi bước, chúng ta chắc chắn rằng không có bộ phận nào
của cây ngoài “cây con hiện hành” có thể chứa các mẩu tin với khóa k, và “cây con hiện hành” ngày càng nhỏ
hơn. Thủ tục sẽ dừng khi có một mẩu tin với khóa k được tìm thấy hoặc “cây con hiện hành” trở nên trống.
Nghĩa là không có một mẩu tin nào chứa khóa k
Trong tìm kiếm nhị phân đã xét ở phần trước. Chúng ta dùng một cây nhị phân để mô tả dãy của các phép so
sánh được tạo bởi một hàm tìm kiếm trong mảng. Ở phần tìm kiếm cây nhị phân này, chúng ta xây dựng một
cấu trúc dữ liệu gồm các mẩu tin được liên kết với nhau và dùng cấu trúc dữ liệu này cho việc tìm kiếm.
Xét hàm tìm kiếm cây nhị phân sau:
Type lienket = ↑ diem;
diem = record
khoa, thongtin: integer;
l, r : lienket;
end;
var t, z, dau: lienket;
function timkiemcaynhiphan(k : integer; x : lienket): lienket;


begin
z ↑.khoa:= k;
repeat
if (k< x ↑.khoa)then
x:= x ↑.l
else x: = x↑.r;
untilk = x↑.khoa;
timkiemcaynhiphan:= x;
end;
end;
Trong hàm trên ta quy ước: liên kết bên phải của nút dau trỏ tới nút gốc của cây và khóa của nút dau nhỏ hơn
tất cả các nút của các khóa khác (thường thì ta cho luôn khóa của nút dau bằng 0 và giả sử tất cả các khóa khác
đều nguyên). Như vậy liên kết trái của dau sẽ không được dùng. Bạn sẽ thấy được sự cần thiết của nút dau khi
ta dùng nó để thao tác trong hàm chèn sau này.
Như vậy theo thuật toán trên thì để tìm kiếm một mẩu tin có khóa k, chúng ta cho x:= timkiemcaynhiphan (x,
dau). Nếu một nút không có cây con trái (hay phải) thì liên kết trái (hay phải ) của nó được trỏ tới nút đuôiz.
Ta đã xét trong trường hợp tìm kiếm tuần tự, chúng ta đã đặt giá trị dạng muốn tìm ở trong z để dừng một quá
trình tìm kiếm không thành công. Do đó “cây con hiện hành” trỏ tới x không bao giờ trở thành rỗng và mọi quá
trình tìm kiếm đều “thành công”. Trong chương trình gọi hàm tìm kiếm có thể kiểm tra liên kết được trả về có
trỏ đến nút z hay không để xác định quá trình tìm kiếm có thành công hay không.
Không mất tính tổng quát, ta quy ước:
- Các liên kết trỏ tới z như trỏ tới các nút ngoài.
- Tất cả các quá trình tìm kiếm không thành công đều kết thúc ở nút ngoài
- Các nút thông thường có chứa các khóa gọi là các nút trong Chú ý: Khi ta đưa thêm khái niệm nút ngoài thì lẽ
ra mỗi nút trong đều trỏ đến hai nút khác của cây. Song về mặt cài đặt chúng ta cho tất cả các nút biểu diễn chỉ
bởi một nút z duy nhất.
Trường hợp cây rỗng sẽ được biểu diễn bởi một liên kết phải của nút dau trỏ tới z được tạo lập bởi đoạn
chương trình sau:
procedure khoitaocay;
begin

new (z);
z↑.l := z↑.r;
z↑.r := z;
new(dau);
dau ↑. khoa := 0;
dau ↑.r := z;
End.
Khởi tạo này trỏ các liên kết của z đến chính nó. Chúng ta sẽ sử dụng thủ tục khởi tạo để đảm bảo an toàn trong
các chương trình nâng cao sau này.
Xét ví dụ áp dụng hàm tìm kiếm khóa I trong cây nhị phân sau:
Trước tiên I được so sánh với A ở gốc. Vì I lớn hơn nên nó tiếp tục được so sánh với S. Cứ tiếp tục như thế nó
sẽ được so sánh với E, R, và cuối cùng là H. Các liên kết trong nút chứa H trỏ tới z và quá trình tìm kiếm kết
thúc: I được so sánh với chính nó ở trong z và kết quả tìm kiếm kết thúc không thành công.
Ta quy ước rằng các liên kết trỏ tới z như là trỏ tới các nút ngoài, tất cả các quá trình tìm kiếm không thành
công đều kết thúc ở các nút ngoài. Các nút không thành công có chứa các khoá được gọi là nút trong; khi đưa
thêm khái niệm nút ngoài thì lẽ ra mỗi nút trong đều phải trỏ đến hai nút khác của cây nhưng về mặt cài đặt
chúng ta cho tất cả các nút ngoài được biểu diễn chỉ bởi nút z duy nhất.
Chúng ta sẽ cùng xét ví dụ sau để thấy các liên kết này và các nút giả một cách rõ nhất:
Chúng ta cùng xét một ví dụ cụ thể sau:
Tìm khoá I trên cây trong hìnhtrên bằng cách dùng thủ tục timkiemcaynhiphan. Trước tiên I được so sánh với
khoá A ở gốc. Vì I lớn hơn nên nó tiếp tục được so sánh với S, cứ tiếp tục như thế I sẽ được so sánh với E, R,
và cuối cùng là H. Các liên kết trong nút chứa H trỏ tới z và quá trình tìm kiếm kết thúc: I được so sánh với
chính nó ở trong z và tìm kiếm kết thúc không thành công.
Chèn nút trong cây nhị phân
Để chèn thêm một nút mới vào cây (giả sử chúng ta đã tìm kiếm nó không thành công, kế đến gặp nó trong nơi
chứa z), chúng ta cần một thủ tục duy trì cha p của x trong khi duyệt cây từ gốc đến ngọn. Khi tiếp xúc với ngọn
của cây (xảy ra khi x=z) p trỏ tới nút mà liên kết của nó phải được thay đổi để trỏ tới nút mới được chèn vào.
function chentrongcay(k: integer; x: lienket): lienket;
var p: lienket;
begin

Repeat p:=x;
if (k = x↑.khoa) then
x:= x↑.lelsex:= x↑.r;
untilx = z;
new(x);x↑.k:= v;
x↑.l: = z;x↑.r := z;
if (v= p↑.khoa)thenp↑.l:= x;else p↑.r:= x;
chentrongcay:=x;
End;
Một khóa k có thể được thêm vào cây bằng cách gọi hàm chentrongcay(k, dau). Hàm này trả về một liên kết tới
nút mới được tạo sao cho thủ tục gọi có thể đặt các giá trị thích hợp vào trường infor
Khi chèn một nút mới có khoá bằng với một khoá nào đó đã có sẵn trong cây, nút mới sẽ được chèn vào bên
phải của nút đã có sẵn. Tất cả các mẩu tin có khoá bằng với k có thể được xử lý bằng cách đặt liên tục t vào thủ
tục timkiem(k, t) như chúng ta đã làm trong tìm kiếm tuần tự.
Ví dụ:
Thấy rằng cây này có được khi chèn các khoá ASEARCHI vào một cây trống đã được khởi động.
Chú ý: Vị trí các khoá bằng nhau trong cây - dù một cây có tới 3 khoá trùng nhau trải dài trong cây nhưng sẽ
không có khoá nào “xen giữa” chúng.
Khi dùng các cây tìm kiếm nhị phân thì hàm sắp xếp (sapxep) sẽ tự động có được nhờ vào cấu trúc của cây. Nếu
quan sát kỹ, các bạn sẽ thấy các khoá được sắp xếp theo thứ tự từ trái qua phải (không kể chiều cao và các liên
kết). Vậy chúng ta có thể suy ra một phương pháp sắp xếp từ các tính chất của cây tìm kiếm nhị phân nhờ quá
trình duyệt cây theo một thứ tự xác định.
procedure hiencay(x : lienket);
begin
if ( x<>z)then
Begin hiencay(x↑.l);
hiendiem(x);hiencay(x↑.r)
end;
End
Việc gọihàm hiencay(dau ↑.r) sẽ in ra các khóa của cây theo thứ tự. Quá trình này đưa ra một phương pháp sắp

xếp tương tự như phương pháp Quicksort, trong đó nút gốc của cây đóng vai trò là phần tử phân hoạch của
Quicksort.

×