Tải bản đầy đủ (.docx) (27 trang)

Tiểu luận Cấu trúc dữ liệu và giải thuật Đề tài:

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 (250.42 KB, 27 trang )

DANH SÁCH THÀNH VIÊN

Họ và tên

Mã sinh viên

1. Hoàng Minh Tâm

11146192

2. Phạm Nguyễn Tiến

11144316

3. Lại Thị Thanh Trang

11144538

4. Đoàn Thị Tố Uyên

11144942

1


MỤC LỤC

LỜI NÓI ĐẦU
Ngày nay, khi ngành công nghệ thông tin ngày càng phát triển, khoa học máy tính
không ngừng vươn tới những tìm tòi mới mẻ hơn. Mọi người chủ yếu làm việc dựa trên
máy móc và thiết bị điện tử thì các phần mềm ứng dụng lại càng trở nên quan trọng và


hữu ích hơn bao giờ hết. Tất cả thông tin muốn biết, muốn tìm hiểu, bạn đều có thể tìm
được trên Internet thông qua các công cụ tìm kiếm. Các công cụ tìm kiếm đó được xây
dựng từ các phần mềm tìm kiếm khác nhau. Và những chương trình tìm kiếm chúng tôi
đề cập đến trong tiểu luận này gồm:


Tìm kiếm tuần tự



Tìm kiếm nhị phân



Tìm kiếm trên cây nhị phân tìm kiếm

2


CHƯƠNG I: GIỚI THIỆU VÀ PHÁT BIỂU ĐỀ TÀI
I.

Phát biểu đề tài
Trong cuộc sống hàng ngày của chúng ta, việc tìm kiếm là không thể thiếu. chẳng hạn

như việc tìm một cuốn sách trên giá sách, một mặt hàng để mua, một người bạn trên
facebook,... điều đó cũng được thể hiện trong các bài toán tin học như tìm xem trong dãy
số đã cho có số nào bằng một giá trị X cho trước hay không...?
Bài toán tìm kiếm có thể phát biểu như sau: Cho một bảng gồm n bản ghi R 1, R2,
R3,..., Rn. Mỗi bản ghi Ri (1 ≤ i ≤ n) có một khóa K i tương ứng. Hãy tìm bản ghi có giá trị

khóa tương ứng bằng X cho trước?
X được gọi là khóa tìm kiếm hay đối trị tìm kiếm. Quá trình tìm kiếm sẽ kết thúc khi
xảy ra một trong hai trường hợp sau:
- Trường hợp 1: Tìm được bản ghi có giá trị khóa tương ứng bằng X, khi đó ta nói
phép tìm kiếm được thỏa (tìm thấy).
- Trương hợp 2: Không tìm được bản ghi nào có giá trị khóa tương ứng bằng X cả,
khi đó ta nói phép tìm kiếm không thỏa (không tìm thấy). Khi phép tìm kiếm không thỏa
thường xuất hiện yêu cầu bổ sung thêm bản ghi mới có khóa bằng X vào, giải thuật thể
hiện yêu cầu này được gọi là “Tìm kiếm có bổ sung”.
Ở đây, ta xét các phương pháp tìm kiếm phổ biến thực hiện tìm kiếm trên dữ liệu
được lưu trữ ở bộ nhớ trong mà ta gọi là tìm kiếm trong. Không giảm tính tổng quát, trong
các giải thuật ta chỉ xét việc tìm kiếm các khóa đại diện cho các bản ghi tương ứng.
II.

Phạm vi, đối tượng nghiên cứu
Trước hết, chúng ta cần hiểu “giải thuật tìm kiếm” là gì? Trong ngành khoa học máy

tính, một giải thuật tìm kiếm là một thuật toán lấy đầu vào là một bài toán và trả về kết
quả là một lời giải cho bài toán đó, thường là sau khi cân nhắc giữa một loạt các lời giải
có thể. Hầu hết các thuật toán được nghiên cứu bởi các nhà khoa học máy tính để giải
quyết các bài toán đều là các thuật toán tìm kiếm. Có 3 loại tìm kiếm là: tìm kiếm không
3


có thông tin, tìm kiếm có thông tin và tìm kiếm đối kháng. Trong đây, ta chỉ đề cập đến
tìm kiếm không có thông tin. Tìm kiếm không có thông tin bao gồm : tìm kiếm trên danh
sách và tìm kiếm trên cây. Tìm kiếm trên danh sách lại chia thành 2 loại : Tìm kiếm tuyến
tính (hay còn gọi là tìm kiếm tuần tự), tìm kiếm nhị phân, tìm kiếm nội suy, bảng băm
nhưng chúng ta chỉ xét tìm kiếm tuần tự và tìm kiếm nhị phân.
a) Tìm kiếm tuần tự: Tìm kiếm tuần tự (tiếng Anh Sequential search) hay tìm kiếm tuyến


tính (tiếng Anh linear search) là một 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 hay đã duyệt qua toàn bộ danh sách. Nó có thời gian chạy khá
lớn: O(n), trong đó n là số phần tử trong danh sách, nhưng có thể sử dụng thẳng cho một
danh sách bất kỳ mà không cần tiền xử lý.
b) Tìm kiếm nhị phân

Thuật toán tìm kiếm nhị phân dùng để tìm kiếm phần tử trong một danh sách đã được
sắp xếp, ví dụ như trong một danh bạ điện thoại sắp xếp theo tên, có thể tìm kiếm số điện
thoại của một người theo tên người đó.
Thuật toán tìm kiếm nhị phân chạy nhanh hơn tìm kiếm tuần tự nhưng cũng có một số
nhược điểm. Tìm kiếm nhị phân có thể chậm hơn bảng băm. Nếu nội dung danh sách bị
thay đổi thì danh sách phải được sắp xếp lại trước khi sử dụng tìm kiếm nhị phân. Thao
tác này thường tốn nhiều thời gian.
Thời gian thực thi: Sau mỗi phép so sánh, số lượng phần tử trong danh sách cần xét
giảm đi một nửa. Thuật toán kết thúc khi số lượng phần tử còn không quá 1. Vì vậy thời
gian thực thi của thuật toán là O(log2n).
c) Tìm kiếm trên cây trên cây nhị phân tìm kiếm


Cây: Trong khoa học máy tính, cây là một cấu trúc dữ liệu được sử dụng rộng rãi gồm
một tập hợp các nút (tiếng Anh: node) được liên kết với nhau theo quan hệ cha - con. Cây
trong cấu trúc dữ liệu đầu tiên là mô phỏng (hay nói cách khác là sự sao chép) của cây (có
gốc) trong lý thuyết đồ thị. Hầu như mọi khái niệm trong cây của lý thuyết đồ thị đều
4


được thể hiện trong cấu trúc dữ liệu. Tuy nhiên cây trong cấu trúc dữ liệu đã tìm được
ứng dụng phong phú và hiệu quả trong nhiều giải thuật. Khi phân tích các giải thuật trên

cấu trúc dữ liệu cây, người ta vẫn thường vẽ ra các cây tương ứng trong lý thuyết đồ thị.


Các nút:

o

Nút gốc: Trong mỗi cây có một nút đặc biệt được gọi là nút gốc (hay nói đơn giản là gốc).
Nút gốc là nút duy nhất không có nút cha. Nút gốc là nơi khởi đầu của nhiều giải thuật
trên cây. Tất cả các nút khác được nối về nút gốc bằng một đường đi qua các cạnh hay các
liên kết

o

Nút lá: Các nút không có nút con được gọi là nút lá (lá)

o

Các nút trong: nút trong của một cây là nút trên cây có ít nhất một con, nghĩa là các nút
không phải là lá. Các khái niệm về mức của mỗi nút, chiều cao của cây được định nghĩa
giống như cây trong lý thuyết đồ thị.



Cây con: Một cây con là một bộ phận của cấu trúc dữ liệu cây mà tự nó cũng là một cây.
Một nút bất kỳ trong cây T, cùng với các nút dưới nó tạo thành một cây con của T.



Cây trong lý thuyết đồ thị: Trong lý thuyết đồ thị, một cây là một đồ thị liên thông và

không có chu trình. Cây như vậy còn được gọi là cây tự do



Cây sắp thứ tự: Có hai dạng cấu trúc cơ sở của cây là cây không thứ tự và cây có thứ tự.
Một cây không thứ tự là cây có cấu trúc cây, trong đó giữa các con của một nút, không có
thứ tự nào. Một cây, trong đó các con của một nút tuân theo một thứ tự xác định được gọi
là cây có thứ tự. Cây tìm kiếm nhị phân là một cây sắp thứ tự điển hình.



Cây tổng quát và cây nhị phân: Các cây trong đó mỗi nút có thể có nhiều hơn hai con
được gọi là cây tổng quát, các cây trong đó mỗi nút có không quá hai con được gọi là cây
nhị phân.



Cây nhị phân tìm kiếm:

5


• Cây tìm kiếm ứng với n khóa k1,k2,...kn là cây nhị phân mà mỗi nút đều được gán
một khóa sao cho với mỗi mỗi nút k:
o
o

Mọi khóa trên cây con trái đều nhỏ hơn khóa trên nút k.
Mọi khóa trên cây con phải đều lớn hơn khóa trên nút k .
• Cây tìm kiếm nhị phân là một cấu trúc dữ liệu cơ bản được sử dụng để xây dựng

các cấu trúc dữ liệu trừu tượng hơn như các tập hợp, đa tập hợp, các dãy kết hợp.


Duyệt cây nhị phân:

Khi xét một cây nhị phân, mỗi đỉnh cùng với các đỉnh đứng sau nó là gốc của một cây
con. Ta xét một đỉnh A là đỉnh trong của cây nhị phân. Theo thứ tự người ta xem xét thứ
tự thăm đỉnh A so với việc thăm hai con của nó là thăm A trước rồi 2 con sau, thăm A xen
giữa việc thăm hai con, thăm A sau khi thăm hai con:
A, con trái, con phải
Con trái, A, con phải
Con trái, con phải, A
6


Tất nhiên nút không có con nào thì việc thăm con không diễn ra. Còn nếu con L hoặc
con R của A lại là gốc của một cây con, thì việc thăm thay bằng việc duyệt cây con gốc
tại đó.
Từ đó có các phương pháp duyệt tiền thứ tự, trung thứ tự, hậu thứ tự đối với cây nhị
phân có gốc tại đỉnh A như sau:
- Duyệt tiền thứ tự cây con gốc A
Nếu Cây là rỗng: Return
Thăm A
Duyệt tiền thứ tự cây con gốc L
Duyệt tiền thứ tự cây con gốc R
- Duyệt trung thứ tự cây con gốc A
Nếu Cây là rỗng: Return
Thăm A
Duyệt trung thứ tự cây con gốc L
Thăm A

Duyệt trung thứ tự cây con gốc R
- Duyệt hậu thứ tự cây con gốc A
Nếu cây là rỗng: Return
Duyệt hậu thứ tự cây con gốc L
Duyệt hậu thứ tự cây con gốc R
Thăm A

7


III. Mục đích của bài luận
− Xác định ý tưởng ban đầu của giải thuật


Viết giải thuật



Nêu lên độ phức tạp của giải thuật



Nêu ví dụ minh họa

8


CHƯƠNG II: NỘI DUNG ĐỀ TÀI
I.


Tìm kiếm tuần tự
1. Ý tưởng giải thuật

Trong Khoa học máy tính tìm kiếm tuần tự (tiếng Anh Sequential search) hay tìm
kiếm tuyến tính (tiếng Anh linear search) là một 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 hay đã duyệt qua toàn bộ danh sách.
Ý tưởng thuật toán:
Xét dãy số cần tìm có n phần tử: k1, k2, k3,..., kn. Giá trị cần tìm là X.
- Bắt đầu từ khoá đầu tiên, lần lượt so sánh khoá X với khoá tương ứng trong dãy.
- Quá trình tìm kiếm kết thúc khi tìm được khoá thoả mãn hoặc đi đến hết dãy hoặc
gặp điều kiện dừng vòng lặp.
2. Giải thuật

Thuật toán tiến hành so sánh x lần lượt với phần tử thứ nhất, thứ hai, ... của mảng A
cho đến khi gặp được phần tử có khóa cần tìm, hoặc đã tìm hết mảng mà không thấy X.
 Các bước thực hiện giải thuật

Bước 1: i = 1;// Bắt đầu phần tử đầu tiên của dãy
Bước 2: So sánh k[i] với X, có 2 khả năng:
+ k[i] = X: Tìm thấy. Dừng
+ k[i] ≠ X: 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
9


Ngược lại: Lặp lại Bước 2.
 Cài đặt giải thuật


① {nhập dãy khóa k là mảng A với số phần tử n}
Procedure NhapDayKhoa(Var A: Array[1..100] of Integer; var n: Integer);
Var i: Integer;
Begin
Write(‘Nhap so phan tu:’); readln(n);
For i:=0 to n do
Begin
Write(‘Nhap phan tu thu ‘,i); readln(A[i]);
End;
End;
② Function TK_TUANTU(k, n, X);
{Cho dãy khóa k gồm n khóa, giải thuật nhằm tìm kiếm trong dãy xem có khóa nào
bằng X hay không? Nếu có hàm sẽ đưa ra chỉ số tương ứng của khóa đó, nếu không thấy
sẽ đưa ra giá trị 0}
(1) {Khởi tạo}
i:= 1;
(2) {Thực hiện tìm kiếm trong dãy khóa}
While ((i <= n) and (k[i] <> 1)) do i:= i+1;
(3) {Trả ra chỉ số i nếu tìm thấy, nếu không tìm thấy thì chỉ ra giá trị 0. Kết thúc}
If i > n then return(0)
10


else return(i);
Nhận xét:
Trong cài đặt trên đây, nhận thấy mỗi lần lặp của vòng lặp while phải tiến hành kiểm
tra 2 điều kiện (i < n) - điều kiện biên của mảng - và (k[i] <> X) - điều kiện kiểm tra
chính. Nhưng thật sự chỉ cần kiểm tra điều kiện chính (k[i] <> X). Để cải tiến cài đặt có
thể dùng phương pháp "lính canh" - đặt thêm 1 phần tử có giá trị X vào cuối mảng, như
vậy bảo đảm luôn tìm thấy X trong mảng, sau đó dưạ vào vị trí tìm thấy để kết luận.

Ta có thể tiến hành cải tiến cài đặt bằng phương pháp “lính canh” như sau:
Function TK_TUANTU_LINHCANH(k, n, X);
{Cho dãy khóa k gồm n khóa, trong giải thuật sử dụng thêm một khóa phụ có giá trị
bằng X và đặt ở cuối dãy, nghĩa là: k[n+1] = X. Giải thuật nhằm tìm kiếm trong dãy xem
có khóa nào bằng X không?Nếu có hàm sẽ đưa ra chỉ số tương ứng của khóa đó, nếu
không thấy sẽ đưa ra giá trị 0}
(1) {Khởi tạo}
k[n+1]:= X;
i:= 1;
(2) {Thực hiện tìm kiếm trong dãy khóa}
While k[i] <> X do i:=i+1;
(3) {Trả ra chỉ số i nếu tìm thấy, nếu không tìm thấy thì trả ra giá trị 0. Kết thúc}
If i < n+1 then return(i);
Else return(0);
3. Phân tích và đánh giá độ phức tạp của giải thuật

Sau đây ta sẽ phân tích độ phức tạp của hai thuật toán đã nêu ở trên và đánh giá độ
phức tạp bằng cách đếm số phép so sánh.
 Thuật toán ban đầu

- Trường hợp tốt nhất khi phần tử đầu tiên của mảng có giá trị bằng X:
Số phép gán: Gmin = 1
Số phép so sánh: Smin = 2 + 1 = 3
=> Độ phức tạp T = O(1)
- Trường hợp xấu nhất khi không tìm thấy phần tử nào có giá trị bằng X:
11


Số phép gán: Gmax = 1
Số phép so sánh: Smax = 2n+1

=> Độ phức tạp T = O(n)
- Trung bình:
Số phép gán: Gtb = 1
Số phép so sánh: Stb = n + 2
=> Độ phức tạp T = O(n)
 Thuật toán cải tiến

- Trường hợp tốt nhất khi phần tử đầu tiên của mảng có giá trị bằng X:
Số phép gán: Gmin = 2
Số phép so sánh: Smin = 1 + 1 = 2
=> Độ phức tạp: T = O(1)
- Trường hợp xấu nhất khi không tìm thấy phần tử nào có giá trị bằng X:
Số phép gán: Gmax = 2
Số phép so sánh: Smax = (n + 1) + 1 = n + 2
=> Độ phức tạp: T = O(n)
- Trung bình:
Số phép gán: Gtb = 2
Số phép so sánh: Stb = (2 + n+2)/2 = n/2 + 2
=> Độ phức tạp: T = O(n)

12


Như vậy, nếu thời gian thực hiện phép gán không đáng kể thì thuật toán cải tiến sẽ
chạy nhanh hơn thuật toán ban đầu.
4. Ví dụ minh họa

Sau đây, ta sẽ minh họa giải thuật tìm kiếm tuần tự qua ví dụ sau:
Cho dãy khóa:
12

8
2
Tìm X = 2 và X = 10?

14

3

5

20

11

30

28

- Với X = 2, ta thực hiện các bước tìm kiếm như sau:
+ So sánh X với 12: X ≠ 12 => Chuyển sang khóa tiếp theo i = 2.
+ So sánh X với 8: X ≠ 8 => Chuyển sang khóa tiếp theo i = 3.
+ So sánh X với 2: X = 2 => Tìm kiếm được thỏa, trả về kết quả chỉ số i = 3.
=> Khóa có giá trị bằng X nằm ở vị trí thứ 3 trong dãy khóa.
- Với X = 10, ta thực hiện các bước tìm kiếm như sau:
+ So sánh X với 12: X ≠ 12 => Chuyển sang khóa tiếp theo i = 2.
+ So sánh X với 8: X ≠ 8 => Chuyển sang khóa tiếp theo i = 3.
+ So sánh X với 2: X ≠ 2 => Chuyển sang khóa tiếp theo i = 4.
+ So sánh X với 14: X ≠ 14 => Chuyển sang khóa tiếp theo i = 5.
+ So sánh X với 3: X ≠ 3 => Chuyển sang khóa tiếp theo i = 6.
+ So sánh X với 5: X ≠ 5 => Chuyển sang khóa tiếp theo i = 7.

+ So sánh X với 20: X ≠ 20 => Chuyển sang khóa tiếp theo i = 8.
+ So sánh X với 11: X ≠ 11 => Chuyển sang khóa tiếp theo i = 9.
+ So sánh X với 30: X ≠ 30 => Chuyển sang khóa tiếp theo i = 10.

13


+ So sánh X với 28: X ≠ 28 => Chuyển sang khóa tiếp theo i = 11, nhưng n = 10
< 11 => Tìm kiếm không được thỏa.
=> Không tìm thấy khóa có giá trị bằng X.
II.

Tìm kiếm nhị phân
1. Ý tưởng giải thuật

Thuật toán tìm kiếm tuần tự tỏ ra đơn giản và thuận tiện trong trường hợp số phần tử
của dãy không lớn lắm. Tuy nhiên, khi số phần tử của dãy khá lớn, chẳng hạn chúng ta
cần tìm một từ nào đó trong từ điển hay tìm kiếm tên một khách hàng nào đó trong danh
bạ điện thoại của một thành phố lớn thì thực hiện thuật toán tìm kiếm tuần tự quả thực
mất rất nhiều thời gian. Trong thực tế, thông thường các phần tử của dãy đã có một thứ tự,
do vậy thuật toán tìm nhị phân sau đây sẽ rút ngắn đáng kể thời gian tìm kiếm trên dãy đã
có thứ tự. Ta vẫn quy ước dãy khóa đã được sắp xếp theo chiều tăng dần đối với giá trị số
và thứ tự từ điển với các giá trị ký tự. Khóa được lấy ra để so sánh với khóa tìm kiếm luôn
luôn chọn khóa “ở giữa” dãy khóa đang xét. Giả sử dãy khóa đang xét là k l, kl+1,...,kr thì
khóa “ở giữa” dãy sẽ là km với m =(nghĩa là phép toán (l+r) div 2).
Đầu tiên, ta xét dãy khóa ban đầu kl, kl+1,..., kr.
So sánh giá trị X với giá trị phần tử giữa của dãy là km, xảy ra 3 trường hợp sau:




Nếu X = km thì phép tìm kiếm được thỏa, khi đó đưa ra chỉ số m tương ứng.
Nếu X < km, phép tìm kiếm sẽ được thực hiện tiếp với dãy ở trước khóa k m là kl, kl+1,...,km.
Nếu X > km, phép tìm kiếm sẽ được thực hiện tiếp với dãy ở sau khóa km là km+1,km+2,...,kr.
1



Lặp lại quá trình này cho đến khi tìm thấy phần tử có giá trị X hoặc dãy khóa xét là
rỗng (tức là không tìm thấy).
2. Giải thuật

Với ý tưởng của giải thuật như trên, sau đây ta đi cài đặt giải thuật dưới hai dạng:
dạng đệ quy và dạng không đệ quy (dạng lặp).
a. Giải thuật ở dạng đệ quy
 Các bước thực hiện giải thuật:
14


B1: Nhập vào một dãy khóa đã được sắp xếp.
B2: If (l > r)
B2.1: Không tìm thấy.
B2.2: Thực hiện bước kế tiếp.
B3: Tính chỉ số giữa m theo công thức: m =
B4: If (X = k[m])
B4.1: Tìm thấy tại vị trí m
B4.2: Thực hiện bước kế tiếp
B5: If (X < k[m])
Tìm đệ quy trên dãy khóa kl, kl+1,..., km-1
B6: If (X > k[m])
Tìm đệ quy trên dãy khóa km+1, km+2,..., kr

B7: Kết thúc.
 Cài đặt giải thuật:
① {nhập dãy khóa k là mảng A với số phần tử n}
Procedure NhapDayKhoa(Var A: Array[1...100] of Integer; var n: Integer);
Var i: Integer;
Begin
Write(‘Nhap so phan tu:’); readln(n);
For i:=0 to n do
Begin
Write(‘Nhap phan tu thu ‘,i); readln(A[i]);
End;
End;
② Function TKNF_DQ(k,l,r,X);
{Dãy khóa k gồm n khóa đã được sắp xếp theo chiều tăng dần, trong giải thuật sử
dụng các biến l, r, m – tương ứng là chỉ số dưới, chỉ số trên và chỉ số giữa của dãy khóa
đang xét (ban đầu l = 1, r = n); trong giải thuật sử dụng thêm một biến vitri để đưa ra chỉ
số của khóa tương ứng nếu tìm thấy, nếu không tìm thấy thì vitri có giá trị 0}
(1) If (l > r) then vitri:=0;
(2) else {dãy khóa đang xét có chỉ số dưới nhỏ hơn hoặc bằng chỉ số trên}
Begin
{Tính chỉ số giữa m theo công thức}
m:= (l+r) div 2;
{Thực hiện so sánh để tìm kiếm trong dãy khóa}
Case X < k[m]:
vitri:= TKNF_DQ(k,l,m-1,X);
X > k[m]:
vitri:= TKNF_DQ(k,m+1,r,X);
else
vitri:= m;
end case;

end;
15


(3) return(vitri);
 Phân tích thuật toán đệ quy:

b.


- Trường hợp tốt nhất khi phần tử ở giữa của mảng có giá trị bằng X:
Số phép gán: Gmin = 1
Số phép so sánh: Smin = 2
- Trường hợp xấu nhất khi không tìm thấy phần tử nào có giá trị bằng X:
Số phép gán: Gmax = + 1
Số phép so sánh: Smax = 3 + 1
- Trung bình:
Số phép gán: Gtb = ½ + 1
Số phép so sánh: Stb = ½(3 + 3)
Giải thuật ở dạng lặp(không đệ quy)
Các bước thực hiện giải thuật
B1:khởi tạo: l = 1
r=n
B2: If (l > r)
B2.1: không tìm thấy
B2.2: thực hiện bước kế tiếp
B3: m =
B4: If (X = k[m])
B4.1: Tìm thấy tại vị trí m
B4.2: Thực hiện bước kế tiếp

B5: If (X < m)
B5.1: r = m – 1
B5.2: lặp lại B2
B6: If (X > m)
B6.1: l = m+1
B6.2: lặp lại B2
B7: Kết thúc.

 Cài đặt giải thuật

① {nhập dãy khóa k là mảng A với số phần tử n}
16


Procedure NhapDayKhoa(Var A: Array[1...100] of Integer; var n: Integer);
Var i: Integer;
Begin
Write(‘Nhap so phan tu:’); readln(n);
For i:=0 to n do
Begin
Write(‘Nhap phan tu thu ‘,i); readln(A[i]);
End;
End;
② Function TKNF_LAP(k,n,X);
{Cho dãy khóa k gồm n khóa đã được sắp xếp theo chiều tăng dần, trong giải thuật
sử dụng các biến l, r, m – tương ứng là chỉ số dưới, chỉ số trên và chỉ số giữa của dãy
khóa đang xét (ban đầu l = 1, r = n); trong giải thuật sử dụng thêm một biến vitri để đưa ra
chỉ số của khóa tương ứng nếu tìm thấy, nếu không tìm thấy thì vitri có giá trị 0}
(1) {Khởi tạo}
l:= 1;

r:= n;
vitri:= 0;
(2) {Thực hiện tìm kiếm}
While l <= r do
Begin
{Tính chỉ số giữa m theo công thức}
m:= (l+r) div 2;
{Thực hiện so sánh để tìm kiếm trong dãy khóa}
Case X < k[m]:
r:= m – 1;
X >k[m]:
l:= m + 1;
else
vitri:= m;
end case;
end;
(3) return(vitri);
 Phân tích thuật toán lặp
- Trường hợp tốt nhất khi phần tử ở giữa của mảng có giá trị bằng X:
Số phép gán: Gmin = 3
Số phép so sánh: Smin = 2
- Trường hợp xấu nhất khi không tìm thấy phần tử nào có giá trị bằng X:
Số phép gán: Gmax = 2 + 4
Số phép so sánh: Smax = 3 + 1
- Trung bình:
Số phép gán: Gtb = + 3.5
17


Số phép so sánh: Stb = ½(3 + 3)

3. Độ phức tạp của giải thuật

Để đánh giá độ phức tạp của giải thuật, ta dựa vào phép so sánh. Từ các bước phân
tích thuật toán ở trên, ta có thể dễ dàng xác định được độ phức tạp của thuật toán là:
- Trong trường hợp tốt nhất: Tt(n) = O(1)
- Trong trường hợp xấu nhất: Tx(n) = O()
- Trong trường hợp trung bình: Ttb(n) = O()
4. Ví dụ minh họa

Ta sẽ minh họa giải thuật tìm kiếm nhị phân với các khóa tìm kiếm X = 5 và X = 23
đối với dãy khóa gồm 10 phần tử đã sắp xếp sau:
1

3

4

5

8

15

17

22

25

30


- Với X = 5: các bước thực hiện tìm kiếm như sau:
[1
[1

3
3

4
4
[4

5
5]
5]
[5]
[5]

8

15

17

22

25

30


=> Phép tìm kiếm được thỏa
- Với X = 23: các bước thực hiện tìm kiếm như sau:

[1

3

4

5

8

15
[15

17
17

22
22
[22

25
25
25
[25
][

30]

30]
30]
30]

=> Phép tìm kiếm không thỏa
Với: Ký hiệu “[“ ứng với l, “]” ứng với r và “][“ là ký hiệu dãy khóa đang xét rỗng.

18


III.

Tìm kiếm trên cây nhị phân tìm kiếm
1. Khái niệm cây nhị phân tìm kiếm

Cây nhị phân tìm kiếm ứng với n khóa đã cho k 1, k2, k3,..., kn là một cây nhị phân mà
mỗi nút của nó được định danh bởi một khóa duy nhất nào đó trong dãy khóa đã cho (mỗi
khóa tương ứng với một nút) và mọi nút trên cây đều thỏa mãn đồng thời hai tính chất
sau:
+ Mọi nút thuộc cây con trái của một nút đều nhỏ hơn nút đó.
+ Mọi nút thuộc cây con phải của một nút đều lớn hơn nút đó.
Ta vẫn quy ước là thứ tự tăng dần đối với dãy khóa là dãy số và thứ tự từ điển đối với
ký tự, xâu ký tự.
Nếu thứ tự là giảm dần đối với dãy khóa thì cây nhị phân tìm kiếm được định nghĩa
như sau: Cây nhị phân tìm kiếm ứng với n khóa đã cho k 1, k2, k3,..., kn là một cây nhị phân
mà mỗi nút của nó được định danh bởi một khóa duy nhất nào đó trong dãy khóa đã cho
(mỗi khóa tương ứng với một nút) và mọi nút trên cây đều thỏa mãn đồng thời hai tính
chất sau:
+ Mọi nút thuộc cây con trái của một nút đều lớn hơn nút đó.
+ Mọi nút thuộc cây con phải của một nút đều nhỏ hơn nút đó.

2. Ý tưởng giải thuật

Cây nhị phân tìm kiếm được lưu trữ móc nối trong máy, có nút gốc được trỏ bởi con
trỏ T. Quy cách của một nút được tổ chức như sau:

TROTRAI

KHOA
DL_KHAC

TROPHAI

Trong đó:
-

Trường TROTRAI, TROPHAI: Là các trường con trỏ tương ứng trỏ tới nút gốc của cây
con trái và nút gốc của cây con phải của nút đó.

-

Trường KHOA: Chứa dữ liệu (thông tin) của nút đó. Trường DL_KHAC lưu trữ các
thông tin khác của bản ghi, không có vai trò trong tìm kiếm.
Quá trình Tìm kiếm như sau: So sánh khóa tìm kiếm X với khóa ở gốc của cây và xảy
ra một trong các trường hợp sau:
19


+ Cây rỗng (T = null): X không có trên cây, phép tìm kiếm không thỏa.
+ X trùng với khóa gốc: Tìm thấy X trên cây, phép tìm kiếm được thỏa.
+ X nhỏ hơn khóa ở gốc: Công việc tìm kiếm được thực hiện tiếp tục bằng cách xét

cây con trái của gốc với kỹ thuật tương tự. Phép tìm kiếm có thể không thỏa hoặc được
thỏa.
+ X lớn hơn khóa ở gốc: Công việc tìm kiếm được thực hiện tiếp tục bằng cách xét
cây con phải của gốc với kỹ thuật tương tự. Phép tìm kiếm có thể không thỏa hoặc được
thỏa.
Trong trường hợp phép tìm kiếm không thỏa, thường xuất hiện yêu cầu bổ sung thêm
một nút mới mà trường KHOA có giá trị bằng X vào cây nhị phân tìm kiếm sao cho tính
chất của cây nhị phân tìm kiếm không thay đổi và ta gọi là giải thuật tìm kiếm có bổ sung.
Giải thuật tìm kiếm có bổ sung trên cây nhị phân tìm kiếm được cài đặt như sau.

3. Giải thuật

Function CAY_TKNF(T,X);
{Giải thuật thực hiện tìm kiếm trên cây nhị phân tìm kiếm (có nút gốc được trỏ bởi T)
xem có nút khóa bằng X hay không? Nếu tìm kiếm được thỏa thì đưa ra con trỏ q trỏ tới
nút đó, nếu tìm kiếm không thỏa thì bổ sung nút mới có khóa bằng X vào cây sao cho vẫn
thỏa mãn tính chất của cây nhị phân tìm kiếm và đưa ra con trỏ q trỏ tới nút mới bổ sung
đó kèm thông báo. Trong giải thuật sử dụng thêm một con trỏ p đi ngay sau con trỏ q}
1. {Khởi tạo}
p:= null;
q:= T;
2. {Thực hiện tìm kiếm}
While q <> null do
Case

X < KHOA(q):

20



p:= q; q:= TROTRAI(q); {Chuyển xuống cây con trái}
X > KHOA(q):
p:= q; q:= TROPHAI(q); {Chuyển xuống cây con phải}
X = KHOA(q):
return(q);
end case;
3. {Tìm không thấy, thực hiện bổ sung}
{Xin cấp phát nút mới}
q <= AVAIL;
KHOA(q):= X;
TROTRAI(q):= TROPHAI(q):= null;
case

T = null:
T:= q; {Bổ sung vào cây rỗng}
X < KHOA(p):
TROTRAI(p):= q; {Bổ sung vào làm nút con trái của nút được trỏ bởi

p}
X > KHOA(p):
TROPHAI(p):= q; {Bổ sung vào làm nút con phải của nút được trỏ bởi
p}
end case;
writeln(‘Không tìm thấy có nút khóa bằng X trên cây. Đã bổ sung.’);
return(q);
21


Chú ý:
Ta có thể sử dụng giải thuật tìm kiếm có bổ sung này để xây dựng cây nhị phân tìm

kiếm ứng với dãy khóa đã cho ban đầu bằng cách liên tục bổ sung các nút ứng với từng
khóa bắt đầu từ một cây rỗng. Đầu tiên ta phải dựng lên nút gốc cây ứng với khóa đầu
tiên (ta quy ước là khóa đầu dãy), trường hợp này ứng với trường hợp tìm kiếm trên cây
rỗng, sau đó với các khóa tiếp theo ta tìm không thấy trên cây thì bổ sung vào cây cho đến
khi khóa cuối cùng của dãy khóa được bổ sung vào thì giải thuật kết thúc và ta được cây
nhị phân tìm kiếm ứng với dãy khóa đã cho.
Dạng của cây nhị phân tìm kiếm hoàn toàn phụ thuộc vào thứ tự của phần tử thuộc
dãy đã cho. Như vậy, với cùng một dãy khóa nếu trình tự các khóa khác nhau sẽ cho ta
các cây nhị phân tìm kiếm khác nhau.
4. Phân tích và đánh giá độ phức tạp

Với giải thuật CAY_NFTK trên, ta thấy dạng của cây nhị phân tìm kiếm được dựng
phụ thuộc hoàn toàn vào dãy khóa đưa vào ban đầu. Như vậy, trong trường hợp tốt nhất,
cây nhị phân tìm kiếm có dạng một cây nhị phân hoàn chỉnh có chiều cao là h = mà số
lượng phép so sánh lại tỉ lệ với chiều cao của cây nên độ phức tạp của giải thuật trong
trường hợp này là O(). Trường hợp xấu nhất xảy ra khi cây nhị phân có dạng một cây nhị
phân suy biến (xảy ra khi dãy khóa đưa vào ban đầu vốn đã có sự sắp xếp rồi), trường hợp
này chính là một danh sách tuyến tính và tìm kiếm trên cây lúc này chính là tìm kiếm tuần
tự có độ phức tạp mà ta đã biết là O(). Trong trường hợp trung bình, người ta cũng chứng
minh được số lượng của phép so sánh chỉ là C tb = 1,386., do đó độ phức tạp của giải thuật
trong trường hợp trung bình cũng chỉ là O(.
5. Ví dụ minh họa

Sau đây, ta minh họa việc tìm kiếm với các khóa tìm kiếm X = 25 và X = 75 trên cây
nhị phân tìm kiếm tương ứng với dãy khóa:
50
30
19
25
90

54
22
Ta có cây nhị phân tìm kiếm tương ứng với dãy khóa trên:

74

80

50

22
19

25 30

54

90
74

80

22


Cây nhị phân tìm kiếm ứng với dãy khóa đã cho

-

Với X = 25, ta sẽ thực hiện tìm kiếm như sau:

+ So sánh X với 50: X < 50 => Chuyển sang tìm kiếm trên cây con trái.
+ So sánh X với 19: X > 19 => Chuyển sang tìm kiếm trên cây con phải.
+ So sánh X với 25: X = 25 => Phép tìm kiếm được thỏa.
-

Với X = 75, ta sẽ thực hiện tìm kiếm như sau:
+ So sánh X với 50: X > 50 => Chuyển sang tìm kiếm trên cây con phải.
+ So sánh X với 90: X < 90 => Chuyển sang tìm kiếm trên cây con trái.
+ So sánh X với 54: X > 54 => Chuyển sang tìm kiếm trên cây con phải.
+ So sánh X với 74: X > 74 => Chuyển sang tìm kiếm trên cây con phải.
+ So sánh X với 80: X < 80 => Chuyển sang tìm kiếm trên cây con trái, nhưng

cây con trái rỗng nên phép tìm kiếm không thỏa và thực hiện bổ sung nút mới có giá
trị khóa bằng 75 vào làm nút con trái của nút có giá trị khóa bằng 80. (Xem hình dưới
đây).

23


50

90

30

54

19

25


74

80

22

75

Cây nhị phân tìm kiếm sau khi đã bổ sung nút có khóa bằng 75

24


CHƯƠNG III: ĐÁNH GIÁ KẾT QUẢ NGHIÊN CỨU VÀ
KẾT LUẬN
I.

Đánh giá kết quả nghiên cứu
Qua việc tìm hiểu các giải thuật tìm kiếm trên, ta có thể đánh giá được các ưu và

nhược điểm của từng giải thuật như sau:
Tìm kiếm tuần tự

Ưu điểm

Nhược điểm

Tìm kiếm nhị phân


Tìm kiếm trên cây
nhị phân tìm kiếm
- có thể thực hiện
trên
mọi
loại
dãy/mảng
- thực hiện tìm kiếm
nhanh

- thao tác đơn giản, - thao tác đơn giản,
dễ thực hiện
dễ thực hiện
- có thể thực hiện - thực hiện tìm kiếm
trên
mọi
loại nhanh hơn so với
dãy/mảng
tìm kiếm tuần tự khi
mảng có kích thước
lớn
Khi dãy/mảng có - chỉ có thể vận giải thuật phức tạp,
kích thước quá lớn, dụng khi dãy/mảng khó thực hiện
tìm kiếm tuần tự tỏ đã có thứ tự
ra rất tốn thời gian
- các thuật toán đệ
quy tốn kém bộ nhớ
để ghi nhận mã lênh
chương trình (mỗi
lần gọi đệ quy) khi

chạy chương trình
nên làm cho chương
trình chạy chậm lại.
Do vậy, trong thực
tế nên dùng thuật
toán không đệ quy

Như vậy, so sánh giữa các thuật toán ta thấy mỗi thuật toán đều có ưu và nhược
điểm riêng. Do đó, khi phải lựa chọn một trong các thuật toán trên, ta cần phải xác
định rõ nhu cầu thực tế để lựa chọn sao cho phù hợp.
II.

Kết luận
Bằng những kiến thức được thầy Lưu Minh Tuấn giảng dạy trên lớp cùng với những

kiến thức có trong giáo trình và sưu tầm trên mạng, chúng em đã xây dựng thành công 3
25


×