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

Tìm kiếm và Sắp xếp

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.76 KB, 24 trang )

Chương 1 Tìm kiếm và Sắp xếp

1.1 Tìm kiếm
Tìm kiếm là thao tác cơ bản, thường xuyên và quan trọng trong tin học. Ví dụ như tìm kiếm
nhân viên trong danh sách nhân viên, tìm kiếm một sinh viên trong danh sách lớp học…Các hệ
thống thông tin thường lưu trữ khối lượng dữ liệu lớn, nên thuật toán tìm kiếm tốt sẽ có nhiều
lợi ích.
Tuy nhiên, thao tác tìm kiếm còn phụ thuộc rất nhiều đến dữ liệu được tổ chức như thế nào;
nếu dữ liệu được tổ chức tốt thì việc tìm kiếm sẽ tiến hành nhanh chóng và hiệu quả hơn. Giả
sử sách được sắp theo chủ đề, thể loại thì dễ tìm kiếm hơn là không được sắp. Hoặc danh sách
tên người được sắp theo thứ tự alphabet cũng dễ cho việc tìm kiếm…
1.1.1 Mô tả bài toán tìm kiếm trong tin học
Tìm kiếm là quá trình xác định một đối tượng nào đó trong một tập các đối tượng. Kết quả
trả về là đối tượng tìm được hoặc một chỉ số (nếu có) xác định vị trí của đối tượng trong tập
đó.
Việc tìm kiếm dựa theo một trường nào đó của đối tượng, trường này là khóa (key) của
việc tìm kiếm.
Ví dụ: đối tượng sinh viên có các dữ liệu {MaSV, HoTen, DiaChi,…}. Khi đó tìm kiếm trên
danh sách sinh viên thì khóa thường chọn là MaSV hoặc HoTen.
Thông thường người ta phân làm hai loại tìm kiếm: tìm kiếm tuyến tính hay còn gọi là tuần
tự cho tập dữ liệu bất kỳ; tìm kiếm nhị phân cho tập dữ liệu đã được sắp xếp.
Bài toán tìm kiếm được mô tả như sau:
• Tập dữ liệu được lưu trữ là dãy a
1
, a
2
,..,a
n
. Giả sử chọn cấu trúc dữ liệu mảng để lưu trữ
dãy số này trong bộ nhớ chính, có khai báo: int a[n];
• Khoá cần tìm là x có kiểu nguyên : int x.


Tìm kiếm
Tìm kiếm tuyến tính
Tìm kiếm nhị phân
Tập DL bất kỳ
Tập DL được sắp
Hình 4.1: Phân loại phương pháp tìm kiếm
1.1.2 Tìm kiếm tuyến tính
Ý tưởng chính: duyệt tuần tự từ phần tử đầu tiên, lần lượt so sánh khóa tìm kiếm với khoá
tương ứng của các phần tử trong danh sách (trong trường hợp mô tả trên là so sánh x và a[i]).
Cho đến khi gặp phần tử cần tìm hoặc đến khi duyệt hết danh sách.
Các bước tiến hành như sau :
Bước 1: i = 1 ;
Bước 2: so sánh a[i] với x, có hai khả năng
i. a[i] = x: tìm thấy ⇒ dừng
ii. a[i] <> x: sang bước 3
Bước 3: i = i +1, kiểm tra chỉ số i và kích thước mảng n
i. nếu i>n: hết mảng, không tìm thấy ⇒ dừng
ii. ngược lại: quay lại bước 2
Hàm tìm kiếm tuyến tính đơn giản minh họa bằng ngôn ngữ C/C++.
int Search(int a[], int n, int key)
{
int i =0;
while (i<n) && (key != a[i])
i++;
if (i >= n)
return -1; // tìm không thấy
else
return i; // tìm thấy tại vị trí i
}
1.1.3 Tìm kiếm nhị phân

Phương pháp tìm kiếm nhị phân được áp dụng cho dãy khoá đã có thứ tự: k[1] ≤ k[2] ≤ ... ≤
k[n].
Ý tưởng của phương pháp này như sau:
Giả sử ta cần tìm trong đoạn a[left...right] với khoá tìm kiếm là x, trước hết ta xét phần tử
giữa a[mid], với mid = (left + right)/2.
a
3
a
2
a
1
x ?
a
n
a
n-1
• Nếu a[mid] < x thì có nghĩa là đoạn a[left] đến a[right] chỉ chứa khóa < x, ta tiến
hành tìm kiếm từ a[mid+1] đến a[right].
• Nếu a[mid] > x thì có nghĩa là đoạn a[m] đến a[right] chỉ chứa khoá > x, ta tiến
hành tìm kiếm từ a[left] đến a[mid-1].
• Nếu a[mid] = x thì việc tìm kiếm thành công.
• Quá trình tìm kiếm thất bại nếu left > right.
Các bước tiến hành như sau:
Bước 1: left =1, right = n // tìm kiếm trên tất 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, tìm tiếp trong dãy a[left].. a[mid-1]
right = mid -1;
- a[mid] < x, tìm tiếp trong dãy a[mid+1].. a[right]

left = mid +1;
Bước 3:
Nếu left ≤ right; còn phần tử ⇒ tìm tiếp ⇒ bước 2
Ngược lại: dừng, đã xét hết phần tử ⇒ không tìm thấy.
Ví dụ: cho dãy số gồm 8 phần tử {1, 2, 4, 5, 6, 8, 12, 15} và x = 8
1
Left = 1
X = 8
Right = 8
Mid = 4
Đoạn tìm kiếm
2
4
5
6
8
12
15
1
Left = 5
X = 8
Right = 8
Mid = 6
Đoạn tìm kiếm
2
4
5
6
8
12

15
=
Hình 4.2: Tìm kiếm nhị phân.
Hàm C minh họa cài đặt thuật toán tìm kiếm nhị phân
int BinarySearch(int key)
{
int left = 0, right = n-1, mid;
while (left <= right)
{
mid = (left + right)/ 2; // lấy điểm giữa
if (a[mid] == key) // nếu tìm được
return mid;
if (a[mid] < x) // tìm đoạn bên phải mid
left = mid+1;
else
right = mid-1; // tìm đoạn bên trái mid
}
return -1; // không tìm được
}
1.1.4 Kết luận
• Giải thuật tìm kiếm tuyến tính không phụ thuộc vào thứ tự của các phần tử trong mảng,
do vậy đây là phương pháp tổng quát nhất để tìm kiếm trên một dãy bất kỳ.
• Thuật giải nhị phân dựa vào quan hệ giá trị của các phần tử trong mảng để định hướng
trong quá trình tìm kiếm, do vậy chỉ áp dụng được với dãy đã có thứ tự.
• Thuật giải nhị phân tìm kiếm nhanh hơn tìm kiếm tuyến tính.
• Tuy nhiên khi áp dụng thuật giải nhị phân thì cần phải quan tâm đến chi phí cho việc
sắp xếp mảng. Vì khi mảng được sắp thứ tự rồi thì mới tìm kiếm nhị phân.
1.2 Bài toán sắp xếp
Sắp xếp là quá trình bố trí lại các phần tử của một tập đối tượng nào đó theo một thứ tự
nhất định. Ví dụ như: tăng dần, giảm dần với một dãy số, thứ tự từ điển với các từ...Việc sắp

xếp là một bài toán thường thấy trong tin học, do các yêu cầu tìm kiếm thuận lợi, sắp xếp kết
quả các bảng biểu...
Dữ liệu thường được tổ chức thành mảng các mẫu tin dữ liệu, mỗi mẫu tin thường có một
số các trường dữ liệu khác nhau. Không phải toàn bộ các trường đều tham gia quá trình sắp xếp
mà chỉ có một trường nào đó (hoặc một vài trường) được quan tâm. Người ta gọi trường này là
khoá, việc sắp xếp sẽ được tiến hành dựa vào giá trị khoá này.
Ví dụ: sắp xếp một mảng các số nguyên tăng dần, sắp xếp một danh sách học sinh với điểm thi
giảm dần...
1.3 Một số phương pháp sắp xếp cơ bản
Trong phần này giới thiệu một số phương pháp sắp xếp cơ bản thường được dùng để sắp
xếp một danh sách, mảng dữ liệu.
1.3.1 Phương pháp chọn
Đây là một trong những thuật toán sắp xếp đơn giản nhất. Ý tưởng cơ bản của phương pháp
này được thể hiện như sau:
1. Ở lượt thứ nhất, ta chọn trong dãy khoá k[1..n] ra khoá nhỏ nhất và đổi giá trị nó với
k[1], khi đó k[1] sẽ trở thành khoá nhỏ nhất.
2. Ở lượt thứ hai, ta chọn trong dãy khoá k[2..n] ra khóa nhỏ nhất và đổi giá trị nó cho
k[2].
3. ...
4. Ở lượt thứ i, ta chọn trong dãy khóa k[i..n] ra khóa nhỏ nhất và đổi giá trị nó cho k[i].
5. Tới lượt k-1, ta chọn giá trị nhỏ nhất trong k[n-1] và k[n] ra khoá nhỏ nhất và đổi cho
giá trị cho k[n-1].
Thuật giải SelectionSort: (mã giả, chỉ số 1 là đầu mảng)
begin
for i:= 1 to n-1 do
begin
jmin := i;
for j:=i+1 to n do
if a[j] < a[jmin] then
jmin = j;

if ( jmin <> i)
Swap(a[i], a[jmin])
end.
end.
1.3.2 Phương pháp sắp xếp nổi bọt
Trong thuật toán sắp xếp nổi bọt, dãy khóa sẽ được duyệt từ cuối lên đầu dãy, nếu gặp hai
khóa kế cận ngược thứ tự thì đổi chỗ cho nhau. Sau lần duyệt như vậy, khóa nhỏ nhất trong dãy
khóa sẽ được chuyển về vị trí đầu tiên và vấn đề trở thành sắp xếp dãy khoá từ k[n] đến k[2].
Thuật giải bubblesort: (mả giả, chỉ số 1 là đầu mảng)
begin
for i:=2 to n do
for j:= n downto i do
if (a[j] < a[j-1])
Swap(a[j],a[j-1])
end.
1.3.3 Phương pháp sắp xếp chèn
Xét dãy khóa k[1..n], ta thấy dãy con chỉ gồm mỗi một khoá là k[1] có thể coi là đã sắp xếp
rồi. Xét thêm k[2], ta so sánh nó với k[1], nếu thấy k[2] < k[1] thì chèn nó vào trước k[1]. Đối
với k[3], ta chỉ xét dãy chỉ gồm hai khoá k[1] và k[2] đã sắp xếp và tìm cách chèn k[3] vào dãy
khóa đó để được thứ tự sắp xếp. Một cách tổng quát, ta sẽ sắp xếp dãy k[1..i] trong điều kiện
dãy k[1..i-1] đã sắp xếp rồi bằng cách chèn k[i] vào dãy đó tại vị trí đúng khi sắp xếp.
Thuật giải InsertionSort: (mả giả, chỉ số 1 là đầu mảng)
begin
for i:= 2 to n do
begin
tmp = a[i];
j = i-1;
while (j>0) and (tmp < a[j])
begin
a[j+1] = a[j];// đẩy lùi giá trị k[i] về sau -> tạo khoảng trống

j := j-1;
end
k[j+1] = tmp; // chèn vào khoảng trống.
end
end.
1.3.4 Phương pháp đổi chỗ trực tiếp
Ý tưởng chính: xuất phát từ đầu dãy, tìm những phần tử còn lại không thoả thứ tự sắp xếp với
phần tử đang xét, hoán vị các phần tử tương ứng để thỏa thứ tự. Lặp lại tương tự với các phần tử
tiếp theo của dãy.
Các bước tiến hành như sau:
 Bước 1 : i = 1; // xuất phát từ đầu dãy
 Bước 2 : j = i+1; // tìm các phần tử phía sau i
 Bước 3 :
o While j ≤ n do
 Nếu a[j]< a[i] ⇒ Swap(a[i], a[j]);
 j = j+1;
 Bước 4 : i = i+1;
o Nếu i < n: ⇒ Bước 2
o Ngược lại ⇒ Kết thúc
Ví dụ: cho dãy số a: 10 3 7 6 2 5 4 16


Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×