CHƯƠNG II
SEARCHING TECHNIQUES
TÌM KIẾM
2
Nội dung
1.
Khái quát về tìm kiếm
2.
Tìm tuyến tính (Linear Search)
3.
Tìm nhị phân (Binary Search)
4.
Complexity of algorithms
Tìm kiếm & sắp xếp
2
Khái niệm về tìm kiếm
Tìm kiếm là quá trình tìm một phần tử dữ liệu có một thành
phần khóa (Key), có kiểu dữ liệu là T nào đó, các thành
phần còn lại là thông tin (Info) liên quan đến phần tử dữ
liệu đó cần thỏa mãn điều kiện tìm kiếm.
Mỗi phần tử dữ liệu có cấu trúc dữ liệu như sau:
typedef struct DataElement
{
T
Key;
InfoType Info;
} DataType;
Việc tìm kiếm một phần tử có thể diễn ra trên một dãy/mảng
(tìm kiếm nội) hoặc diễn ra trên một tập tin/ file (tìm kiếm
ngoại).
Tìm kiếm & sắp xếp
3
Các giải thuật
tìm kiếm nội
Tìm
kiếm tuyến tính
Tìm kiếm nhị phân
Tìm kiếm tuyến tính
Tìm kiếm tuyến tính – Tìm kiếm tuần tự
Ý tưởng:
Bắt đầu từ phần tử đầu tiên của danh sách, so sánh lần lượt từng phần tử của
danh sách với giá trị X cần tìm
Nếu có phần tử bằng X thì trả về vị trí tìm thấy, thuật toán dừng lại (thành công)
Nếu đến cuối danh sách mà không có phần tử nào bằng X, thuật toán dừng lại (không thành
công)
Thuật toán:
B1: i = 1 //Duyệt từ đầu mảng
B2: IF M[i] # X AND i<= N //Nếu chưa tìm thấy và cũng chưa duyệt hết mảng
B2.1: i++
B2.2: Lặp lại B2
B3: IF i <= N
Tìm thấy tại vị trí i
B4: ELSE
Không tìm thấy phần tử có giá trị X
B5: Kết thúc
Tìm kiếm & sắp xếp
6
Tìm kiếm tuyến tính
(sequential search)
5
Vị trí = 2
Khóa tìm
0
1
2
3
4
5
6
7
7 13 5 21 6
2
8 15
Tìm thành công
Số lần so sánh: 3
Tìm kiếm & sắp xếp
7
Tìm kiếm tuyến tính
(không tìm thấy)
5
Vị trí kế tiếp : i+1
Khóa tìm
0
1
2
3
4
5
6
7
7 13 5 21 6
2
8 15
Không tìm thấy
Số lần so sánh: 5
Kết luận : TÌm thấy một phần tử tại vị trí 2
Tìm kiếm & sắp xếp
8
Tìm kiếm tuyến tính
(không tìm thấy)
9
Khóa tìm
0
1
2
3
4
5
6
7
7 13 5 21 6
2
8 15
Không tìm thấy
Số lần so sánh: 8
Tìm kiếm & sắp xếp
9
Tìm kiếm tuyến tính
•
Cài đặt thuật toán
int LinearSearch(int a[], int n, int x)
{
int i=0;
while(i
if (i
// a[i] là phần tử có
khoá x
return -1;
// tìm hết mảng nhưng không có x
}
Tìm kiếm & sắp xếp
10
Tìm kiếm tuyến tính
Phân tích thuật toá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 = 1
Số phép so sánh: Smin = 2 + 1 = 3
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 = 2N+1
Trung bình:
Số phép gán: Gavg = 1
Số phép so sánh: Savg = (3 + 2N + 1) : 2 = N + 2
Tìm kiếm & sắp xếp
11
Tìm kiếm tuyến tính
Cải tiến cài đặt: dùng phương pháp “lính canh”
Đặt thêm một phần tử có giá trị x vào cuối mảng
Bảo đảm luôn tìm thấy x trong mảng
Sau đó dựa vào vị trí tìm thấy để kết luận.
Tìm kiếm & sắp xếp
12
Tìm kiếm tuyến tính
B1: i = 0
B2: M[N] = X //Phần tử cầm canh
B3: IF M[i] # X
B3.1: i++
B3.2: Lặp lại B3
B4: IF i < N
Tìm thấy tại vị trí i
B5: ELSE //i = N song đó chỉ là phần tử cầm canh
Không tìm thấy phần tử có giá trị X
B6: Kết thúc
Tìm kiếm & sắp xếp
13
Tìm kiếm tuyến tính
int LinearSearch(int a[], int n, int x)
{
int i=0;
// mảng gồm N phần tử từ a[0]..a[N-1]
a[n] = x;
// thêm lính canh vào cuối dãy
while(a[i]!=x) i++;
if (i
return -1;
// tìm hết mảng nhưng không có x
}
Tìm kiếm & sắp xếp
14
Tìm kiếm tuyến tính
Phân tích 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
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
Trung bình:
Số phép gán: Gavg = 2
Số phép so sánh: Savg = (2 + N + 2) : 2 = N/2 + 2
Tìm kiếm & sắp xếp
15
Tìm kiếm tuyến tính
Đánh giá giải thuật:
Trường hợp
Số lần so sánh
Giải thích
Tốt nhất
1
Phần tử đầu tiên có giá trị x
Xấu nhất
n+1
Phần tử cuối cùng có giá trị x
Trung bình
(n+1)/2
Giả sử xác suất các phần tử trong
mảng nhận giá trị x là như nhau.
Vậy giải thuật tìm tuần tự có độ phức tạp tính toán cấp n: T(n) = O(n)
Tìm kiếm & sắp xếp
16
Tìm kiếm tuyến tính
Nhận xét:
Giải
thuật tìm tuyến tính không phụ thuộc vào thứ tự
của các phần tử trong danh sách, do vậy đây là
phương pháp tổng quát nhất để tìm kiếm trên một
danh sách bất kỳ.
Một thuật toán có thể được cài đặt theo nhiều cách
khác nhau, kỹ thuật cài đặt ảnh hưởng đến tốc độ thực
hiện của thuật toán.
Tìm kiếm & sắp xếp
17
Tìm kiếm nhị phân
Tìm kiếm nhị phân
Đối với những dãy đã có thứ tự (giả sử thứ tự
tăng), các phần tử trong dãy có quan hệ
a[i-1] ≤ a[i] ≤ a[i+1]
Nếu x > a[i] thì x chỉ có thể xuất hiện trong đoạn
[a[i+1] ,a[N]] của dãy
Nếu x < a[i] thì x chỉ có thể xuất hiện trong đoạn
[a[0] ,a[i-1]] của dãy .
Tìm kiếm & sắp xếp
19
Tìm kiếm nhị phân
Ý tưởng:
Phạm vi tìm kiếm ban đầu của chúng ta là từ phần tử đầu tiên
của dãy (First = 1) cho đến phần tử cuối cùng của dãy (Last =
N).
So sánh giá trị X với giá trị phần tử đứng ở giữa của dãy M là
M[Mid].
Nếu X = M[Mid]: Tìm thấy
Nếu X < M[Mid]: Rút ngắn phạm vi tìm kiếm về nửa đầu của dãy
M (Last = Mid–1)
Nếu X > M[Mid]: Rút ngắn phạm vi tìm kiếm về nửa sau của dãy
M (First = Mid+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 phạm vi tìm kiếm của chúng ta không còn nữa (First >
Last).
Tìm kiếm & sắp xếp
20
Tìm kiếm nhị phân
Bước 1: First = VTĐ; Last = VTC;
Bước 2: Trong khi First ≤ Last lặp: //đoạn tìm kiếm chưa rỗng
Bước 21: mid = (First+Last)/2; // lấy mốc so sánh
Bước 22: Nếu a[mid] = x:
//Tìm thấy.
Dừng, vị trí xuất hiện: mid
Bước 23: Nếu a[mid] > x:
//tìm x trong dãy con aFirst...
amid -1
Last = mid - 1;
Ngược lại //tìm x trong dãy con amid +1 .. aLast
First = mid + 1;
//Hết lặp
Bước 3: Dừng, không tìm thấy.
Tìm kiếm & sắp xếp
21
Tìm kiếm nhị phân
Vi trí = 3
10
Khóa cần tìm bằng
nhỏ hơn
lớn
hơn hoặc bằng
Khóa tìm
0
1
2
2
5
8 10 12 13 15 18 21 24
First
3
4
5
6
7
8
mid
9
Last
Tìm thấy
Số lần so sánh: 4
Tìm kiếm & sắp xếp
22
Tìm kiếm nhị phân
int RecBinarySearch (T M[], int First, int Last, T X)//Đệ qui
{
if (First > Last)
return -1;
int Mid = (First + Last)/2;
if (X == M[Mid])
return Mid;
if (X < M[Mid])
return(RecBinarySearch(M, First, Mid – 1, X));
else
return(RecBinarySearch(M, Mid + 1, Last, X));
}
Tìm kiếm & sắp xếp
23
Tìm kiếm nhị phân
Phân tích thuật toán đệ quy:
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 = log2N + 1
Số phép so sánh: Smax = 3log2N + 1
Trung bình:
Số phép gán: Gavg = ½ log2N + 1
Số phép so sánh: Savg = ½(3log2N + 3)
Tìm kiếm & sắp xếp
24
Tìm kiếm nhị phân
Thuật toán không đệ quy:
B1: First = 1, Last = N
B2: IF (First > Last)
B2.1: Không tìm thấy
B2.2: Thực hiện Bkt
B3: Mid = (First + Last)/ 2
B4: IF (X = M[Mid])
B4.1: Tìm thấy tại vị trí Mid
B4.2: Thực hiện Bkt
B5: IF (X < M[Mid])
B5.1: Last = Mid – 1
B5.2: Lặp lại B3
B6: IF (X > M[Mid])
B6.1: First = Mid + 1
B6.2: Lặp lại B3
Bkt: Kết thúc
Tìm kiếm & sắp xếp
25