Chương 2.1. Giải thuật tìm kiếm
ầ
Tr n Minh Thái Email: minhthai@huflit.edu.vn Website: www.minhthai.edu.vn
1
Mục tiêu
• Xác định được vai trò của tìm kiếm và sắp xếp trong hệ thống
• Nắm vững và minh họa được giải thuật tìm tuần tự và tìm kiếm
thông tin
• Cài đặt được giải thuật tìm kiếm bằng ngôn ngữ C/C++
2
nhị phân trên mảng một chiều
Nhu cầu tìm kiếm và sắp xếp
• Tìm kiếm: Có trong hầu hết trong các hệ thống thông tin
• Muốn tìm kiếm nhanh và hiệu quả dữ liệu có thứ tự sắp
3
xếp
Vấn đề tìm kiếm
• Dựa vào một phần thông tin được gọi là khoá (key) tìm một
• Có thể có nhiều mẫu tin hoặc không có mẫu tin nào chứa
mẫu tin (record) chứa các thông tin khác liên quan với khoá này
4
khoá cần tìm
Đánh giá giải thuật tìm kiếm
• Tìm kiếm thường là tác vụ tốn nhiều thời gian trong một
Tổ chức cấu trúc dữ liệu và giải thuật cho việc tìm kiếm ảnh
chương trình
• Thông số đo chủ yếu là số lần so sánh khoá cần tìm với các
hưởng lớn đến hiệu suất hoạt động của chương trình
5
mẫu tin khác
Phân loại
• Tìm kiếm nội và tìm kiếm ngoại
• Dữ liệu lưu trên thiết bị lưu trữ ngoài như đĩa hay băng từ: tìm
• Dữ liệu được lưu trữ trên bộ nhớ chính: tìm kiếm nội
6
kiếm ngoại
Các giải thuật tìm kiếm
• Có 2 giải thuật thường được áp dụng: tìm tuần tự và tìm nhị
• Đặc tả:
phân
a3 a4
a2
a5
…
aN
a1 • Tập dữ liệu được lưu trữ là dãy số a1, a2, ... ,aN.
an- 1
• Khai báo: int a[N];
• Khóa cần tìm: int x;
7
Tìm tuần tự (Linear Search)
Ý tưởng
Lần lượt so sánh x với phần tử thứ nhất, thứ hai, ... của mảng a
8
cho đến khi gặp được phần tử cần tìm, hoặc hết mảng
Tìm tuần tự
• Minh họa tìm x =10
10
Đã tìm Chưa hết thấy tại mảng vị trí 5
12 3
7 1
10 10 5
32 6
13 7
9 8
15 9
3 10
41 5 4 2 • Minh họa tìm x =25
25
Đã hết Chưa hết mảng mảng
7 1
5 2
12 3
41 4
10 5
32 6
13 7
9 8
15 9
3 10
9
Giải thuật
Bước 1:
i = 1; // bắt đầu từ phần tử đầu tiên
của dãy
Bước 2: So sánh a[i] với x, có 2 khả năng :
• a[i] = x : Tìm thấy. Dừng
• a[i] != x : Sang Bước 3.
Bước 3:
• i = i+1; // xét tiếp phần tử kế trong mảng
• Nếu i >N: Hết mảng, không tìm thấy. Dừng
Ngược lại: Lặp lại Bước 2. 10
Nguyên tắc cài đặt hàm tìm kiếm
• Nếu có xuất hiện phần tử có giá trị x thì trả về vị trí tìm được
• Ngược lại thì trả về -1
11
Cài đặt int LinearSearch(int a[], int N, int x)
{
int i=0;
while ((i
i++;
if(i==N)
return -1; //tìm hết mảng
else
return i; //a[i] là phần tử có khoá x
12
}
Vấn đề
int LinearSearch(int a[], int N, int x)
{
Nếu có x thì không cần
thiết
Có thể loại bỏ?
int i=0;
while ((i
i++;
if(i==N)
return -1; //tìm hết mảng
else
return i; //a[i] là phần tử có khoá x
13
}
Cải tiến
• Minh họa tìm x =10
Dùng lính canh giúp giảm bớt phép so sánh
10
10
12
3
7
1
10
10
5
32
6
13
7
15
9
3
10
9
8
11
41
5
4
2
• Minh họa tìm x = 25
25
5
2
12
3
41
4
10
5
32
6
13
7
3
10
15
9
7
1
9
8
25
25
11
14
int i=0;
a[N] = x; // thêm phần tử x sau mảng
while (a[i]!=x )
i++;
return -1; // tìm hết mảng
if (i==N)
else
return i; // tìm thấy x tại vị trí i
}
Độ phức tạp tính toán cấp n: T(n)=O(n)
15
Cài đặt
int LinearSearch2(int a[],int N,int x)
{
Q & A
16
Tìm kiếm nhị phân (Binary Search)
• Áp dụng đối với dãy số đã có thứ tự
• Mỗi bước tiến hành so sánh x với phần tử ở giữa của dãy hiện
Ý tưởng
17
hành để quyết định phạm vi tìm kế tiếp
Minh họa tìm x = 41
x
x
x
14
14
14
14
16
16
16
16
19
19
19
19
41
41
41
41
46
46
46
46
63
63
63
63
3
3
3
3
22
22
22
22
51
51
51
51
71
71
71
71
2
2
2
2
3
3
3
3
4
4
4
4
6
6
6
6
7
7
7
7
9
9
9
9
1
1
1
1
5
5
5
5
8
8
8
8
10
10
10
10
Tìm thấy x tại vị
trí 6
l
m
m
r
m
18
Minh họa tìm x = 45
x
x
x
x
14
14
14
14
14
16
16
16
16
16
19
19
19
19
19
41
41
41
41
41
46
46
46
46
46
63
63
63
63
63
3
3
3
3
3
22
22
22
22
22
51
51
51
51
51
71
71
71
71
71
2
2
2
2
2
3
3
3
3
3
4
4
4
4
4
6
6
6
6
6
7
7
7
7
7
9
9
9
9
9
1
1
1
1
1
5
5
5
5
5
8
8
8
8
8
10
10
10
10
10
l
m
m
r
l > r: Kết thúc:
Không tìm thấy
m
m
19
//tìm tiếp x trong dãy con aleft .. amid -1
right =mid - 1;
• a[mid] < x:
//tìm tiếp x trong dãy con amid +1 .. aright
left = mid + 1;
Bước 3:
Nếu left <= right //còn phần tử chưa xét tìm tiếp.
Lặp lại Bước 2.
Ngược lại: Dừng //Ðã xét hết tất cả các phần tử.
20
Giải thuật
Bước 1: left = 1; right = N; //tìm kiếm tất cả các phần tử
Bước 2:
mid = (left+right)/2; // lấy mốc so sánh
So sánh a[mid] với x, có 3 khả năng :
• a[mid] = x: Tìm thấy. Dừng
• a[mid] > x:
int BinarySearch(int a[],int N,int x )
{
int left =0; right = N-1;
int mid;
while (left <= right)
{
mid = (left + right)/2;
if (x == a[mid])
return mid;//Tìm thấy x tại vị trí mid
if (x < a[mid])
right = mid -1;
else
left = mid +1;
}
return -1; // Tìm hết dãy mà không có x
}
Độ phức tạp tính toán cấp n: T(n)=O(log 2n)
21
Bài tập
• Cài đặt hàm tìm kiếm nhị phân bằng phương pháp đệ quy?
22
Q & A
23
Code minh họa
#include
#include
#include
#define MAX 1000
void TaoMang(int a[], int N);
void XuatMang(int a[], int N);
24
int LinearSearch(int a[], int N, int x);
void main()
{
srand((usigned int) time (NULL));
int a[MAX], N = 20, x, kq;
TaoMang(a, N);
XuatMang(a, N);
cout<<“Nhap gia tri can tim: “;
cin>>x;
kq=LinearSearch(a, N, x);
if(kq==-1)
cout<<“Khong co phan tu can tim”;
else
cout<<“Phan tu can tim tai vi tri: ”<
25
}
void TaoMang(int a[], int N)
{
for(int i=0; i
a[i]=rand()%N;
}
void XuatMang(int a[], int N)
{
i++;
if(i==N)
return -1; //tìm hết mảng
else
return i; //a[i] là phần tử có khoá x
12
}
Vấn đề int LinearSearch(int a[], int N, int x)
{
Nếu có x thì không cần thiết Có thể loại bỏ?
int i=0;
while ((i
i++;
if(i==N)
return -1; //tìm hết mảng
else
return i; //a[i] là phần tử có khoá x
13
}
Cải tiến
• Minh họa tìm x =10
Dùng lính canh giúp giảm bớt phép so sánh
10
10
12
3
7
1
10
10
5
32
6
13
7
15
9
3
10
9
8
11
41
5
4
2
• Minh họa tìm x = 25
25
5
2
12
3
41
4
10
5
32
6
13
7
3
10
15
9
7
1
9
8
25
25
11
14
int i=0;
a[N] = x; // thêm phần tử x sau mảng
while (a[i]!=x )
i++;
return -1; // tìm hết mảng
if (i==N)
else
return i; // tìm thấy x tại vị trí i
}
Độ phức tạp tính toán cấp n: T(n)=O(n)
15
Cài đặt
int LinearSearch2(int a[],int N,int x)
{
Q & A
16
Tìm kiếm nhị phân (Binary Search)
• Áp dụng đối với dãy số đã có thứ tự
• Mỗi bước tiến hành so sánh x với phần tử ở giữa của dãy hiện
Ý tưởng
17
hành để quyết định phạm vi tìm kế tiếp
Minh họa tìm x = 41
x
x
x
14
14
14
14
16
16
16
16
19
19
19
19
41
41
41
41
46
46
46
46
63
63
63
63
3
3
3
3
22
22
22
22
51
51
51
51
71
71
71
71
2
2
2
2
3
3
3
3
4
4
4
4
6
6
6
6
7
7
7
7
9
9
9
9
1
1
1
1
5
5
5
5
8
8
8
8
10
10
10
10
Tìm thấy x tại vị
trí 6
l
m
m
r
m
18
Minh họa tìm x = 45
x
x
x
x
14
14
14
14
14
16
16
16
16
16
19
19
19
19
19
41
41
41
41
41
46
46
46
46
46
63
63
63
63
63
3
3
3
3
3
22
22
22
22
22
51
51
51
51
51
71
71
71
71
71
2
2
2
2
2
3
3
3
3
3
4
4
4
4
4
6
6
6
6
6
7
7
7
7
7
9
9
9
9
9
1
1
1
1
1
5
5
5
5
5
8
8
8
8
8
10
10
10
10
10
l
m
m
r
l > r: Kết thúc:
Không tìm thấy
m
m
19
//tìm tiếp x trong dãy con aleft .. amid -1
right =mid - 1;
• a[mid] < x:
//tìm tiếp x trong dãy con amid +1 .. aright
left = mid + 1;
Bước 3:
Nếu left <= right //còn phần tử chưa xét tìm tiếp.
Lặp lại Bước 2.
Ngược lại: Dừng //Ðã xét hết tất cả các phần tử.
20
Giải thuật
Bước 1: left = 1; right = N; //tìm kiếm tất cả các phần tử
Bước 2:
mid = (left+right)/2; // lấy mốc so sánh
So sánh a[mid] với x, có 3 khả năng :
• a[mid] = x: Tìm thấy. Dừng
• a[mid] > x:
int BinarySearch(int a[],int N,int x )
{
int left =0; right = N-1;
int mid;
while (left <= right)
{
mid = (left + right)/2;
if (x == a[mid])
return mid;//Tìm thấy x tại vị trí mid
if (x < a[mid])
right = mid -1;
else
left = mid +1;
}
return -1; // Tìm hết dãy mà không có x
}
Độ phức tạp tính toán cấp n: T(n)=O(log 2n)
21
Bài tập
• Cài đặt hàm tìm kiếm nhị phân bằng phương pháp đệ quy?
22
Q & A
23
Code minh họa
#include
#include
#include
#define MAX 1000
void TaoMang(int a[], int N);
void XuatMang(int a[], int N);
24
int LinearSearch(int a[], int N, int x);
void main()
{
srand((usigned int) time (NULL));
int a[MAX], N = 20, x, kq;
TaoMang(a, N);
XuatMang(a, N);
cout<<“Nhap gia tri can tim: “;
cin>>x;
kq=LinearSearch(a, N, x);
if(kq==-1)
cout<<“Khong co phan tu can tim”;
else
cout<<“Phan tu can tim tai vi tri: ”<
25
}
void TaoMang(int a[], int N)
{
for(int i=0; i
a[i]=rand()%N;
}
void XuatMang(int a[], int N)
{
i++;
if(i==N)
return -1; //tìm hết mảng
else
return i; //a[i] là phần tử có khoá x
13
}
Cải tiến
• Minh họa tìm x =10
Dùng lính canh giúp giảm bớt phép so sánh
10
10
12 3
7 1
10 10 5
32 6
13 7
15 9
3 10
9 8
11
41 5 4 2 • Minh họa tìm x = 25
25
5 2
12 3
41 4
10 5
32 6
13 7
3 10
15 9
7 1
9 8
25 25 11
14
int i=0;
a[N] = x; // thêm phần tử x sau mảng while (a[i]!=x ) i++;
return -1; // tìm hết mảng
if (i==N) else
return i; // tìm thấy x tại vị trí i
} Độ phức tạp tính toán cấp n: T(n)=O(n)
15
Cài đặt int LinearSearch2(int a[],int N,int x) {
Q & A
16
Tìm kiếm nhị phân (Binary Search)
• Áp dụng đối với dãy số đã có thứ tự
• Mỗi bước tiến hành so sánh x với phần tử ở giữa của dãy hiện
Ý tưởng
17
hành để quyết định phạm vi tìm kế tiếp
Minh họa tìm x = 41
x
x
x
14 14 14 14
16 16 16 16
19 19 19 19
41 41 41 41
46 46 46 46
63 63 63 63
3 3 3 3
22 22 22 22
51 51 51 51
71 71 71 71
2 2 2 2
3 3 3 3
4 4 4 4
6 6 6 6
7 7 7 7
9 9 9 9
1 1 1 1
5 5 5 5
8 8 8 8
10 10 10 10
Tìm thấy x tại vị trí 6
l
m
m
r
m
18
Minh họa tìm x = 45
x
x
x
x
14 14 14 14 14
16 16 16 16 16
19 19 19 19 19
41 41 41 41 41
46 46 46 46 46
63 63 63 63 63
3 3 3 3 3
22 22 22 22 22
51 51 51 51 51
71 71 71 71 71
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
6 6 6 6 6
7 7 7 7 7
9 9 9 9 9
1 1 1 1 1
5 5 5 5 5
8 8 8 8 8
10 10 10 10 10
l
m
m
r
l > r: Kết thúc: Không tìm thấy
m
m
19
//tìm tiếp x trong dãy con aleft .. amid -1
right =mid - 1;
• a[mid] < x:
//tìm tiếp x trong dãy con amid +1 .. aright
left = mid + 1;
Bước 3:
Nếu left <= right //còn phần tử chưa xét tìm tiếp.
Lặp lại Bước 2.
Ngược lại: Dừng //Ðã xét hết tất cả các phần tử.
20
Giải thuật Bước 1: left = 1; right = N; //tìm kiếm tất cả các phần tử Bước 2: mid = (left+right)/2; // lấy mốc so sánh So sánh a[mid] với x, có 3 khả năng : • a[mid] = x: Tìm thấy. Dừng • a[mid] > x:
int BinarySearch(int a[],int N,int x ) {
int left =0; right = N-1;
int mid; while (left <= right) {
mid = (left + right)/2;
if (x == a[mid])
return mid;//Tìm thấy x tại vị trí mid
if (x < a[mid])
right = mid -1;
else left = mid +1;
} return -1; // Tìm hết dãy mà không có x
} Độ phức tạp tính toán cấp n: T(n)=O(log 2n)
21
Bài tập
• Cài đặt hàm tìm kiếm nhị phân bằng phương pháp đệ quy?
22
Q & A
23
Code minh họa
#include
#include
#include
#define MAX 1000
void TaoMang(int a[], int N);
void XuatMang(int a[], int N);
24
int LinearSearch(int a[], int N, int x);
void main()
{
srand((usigned int) time (NULL));
int a[MAX], N = 20, x, kq;
TaoMang(a, N);
XuatMang(a, N);
cout<<“Nhap gia tri can tim: “;
cin>>x;
kq=LinearSearch(a, N, x);
if(kq==-1)
cout<<“Khong co phan tu can tim”;
else
cout<<“Phan tu can tim tai vi tri: ”< 25 } void TaoMang(int a[], int N) { for(int i=0; i a[i]=rand()%N; } void XuatMang(int a[], int N) {