Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 24 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
24
Dung lượng
882,74 KB
Nội dung
Chương 1 Tìm kiếmvàSắpxếp 1.1 Tìm kiếmTìmkiếm là thao tác cơ bản, thường xuyên và quan trọng trong tin học. Ví dụ như tìmkiếm nhân viên trong danh sách nhân viên, tìmkiế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ìmkiếm tốt sẽ có nhiều lợi ích. Tuy nhiên, thao tác tìmkiế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ìmkiế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ìmkiế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ìmkiếm trong tin học Tìmkiế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ìmkiế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ìmkiế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ìmkiếm tuyến tính hay còn gọi là tuần tự cho tập dữ liệu bất kỳ; tìmkiếm nhị phân cho tập dữ liệu đã được sắp xếp. Bài toán tìmkiế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ếmTìmkiếm tuyến tính Tìmkiế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ìmkiếm 1.1.2 Tìmkiế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ìmkiế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ìmkiế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ìmkiếm nhị phân Phương pháp tìmkiế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ìmkiế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ìmkiế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ìmkiếm từ a[left] đến a[mid-1]. • Nếu a[mid] = x thì việc tìmkiếm thành công. • Quá trình tìmkiế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ìmkiế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ìmkiếm 2 4 5 6 8 12 15 1 Left = 5 X = 8 Right = 8 Mid = 6 Đoạn tìmkiếm 2 4 5 6 8 12 15 = Hình 4.2: Tìmkiếm nhị phân. Hàm C minh họa cài đặt thuật toán tìmkiế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ìmkiế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ìmkiế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ìmkiếm nhanh hơn tìmkiế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ắpxếp mảng. Vì khi mảng được sắp thứ tự rồi thì mới tìmkiếm nhị phân. 1.2 Bài toán sắpxếpSắpxế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ắpxếp là một bài toán thường thấy trong tin học, do các yêu cầu tìmkiếm thuận lợi, sắpxế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ắpxế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ắpxếp sẽ được tiến hành dựa vào giá trị khoá này. Ví dụ: sắpxếp một mảng các số nguyên tăng dần, sắpxế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ắpxếp cơ bản Trong phần này giới thiệu một số phương pháp sắpxếp cơ bản thường được dùng để sắpxế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ắpxế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ắpxếp nổi bọt Trong thuật toán sắpxế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ắpxế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ắpxế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ắpxế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ắpxếpvà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ắpxếp dãy k[1 i] trong điều kiện dãy k[1 i-1] đã sắpxế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ắpxế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 [...]... i j 3 a[k] > x, với k = j n Ta có nhận xét khi đó dãy con thứ 2 đã có thứ tự, nếu dãy con 1 và dãy con 3 có một phần tử thì chúng cũng đã có thứ tự, khi đó dãy ban đầu đã được sắp Ngược lại, nếu dãy con 1 và 3 có nhiều hơn một phần tử thì dãy ban đầu có thứ tự khi dãy con 1 và 3 được sắp Để sắpxếp dãy con 1 và 3, ta lần lượt tiến hành việc phân hoạch từng dãy con theo cùng phương pháp vừa trình bày... thức phân loại các con số trong dãy và thứ tự phân loại con con số này để tạo ra thứ tự cho các phần tử Đây là cách tiếp cận khác so với các phương pháp sắpxếp trước là so sánh các giá trị của các phần tử Thuật giải dựa trên ý tưởng chính là sắpxếp từng con số của một dãy các số Giả sử chúng ta có dãy số như sau: 493 812 715 710 195 437 582 340 385 Đầu tiên sắpxếp các số theo hàng đơn vị: 493 812... để tìm vị trí thích hợp trong dãy con x = a[i]; // a[j] đứng trước a[i] trong cùng dãy con j = i – len; while ((x < a[j]) && (j>= 0)) // sắpxếp dãy con chứa x dùng pp chèn { a[j+len] = a[j]; // dời về sau theo dãy con j = j – len; // qua phần tử trước trong dãy con } a[j+len] = x;// đưa x vào vị trí thích hợp trong dãy con } } } 1.3.6 Phương pháp phân đoạn QuickSort Đây là một phương pháp sắp xếp. .. right, o Cho x = a[k], i = left, j = right Bước 2: Tìmvà hoán vị cặp phần tử a[i] và a[j] không đúng thứ tự đang sắp o Bước 2-1: Trong khi a[i] < x ⇒ i++; o Bước 2-2: Trong khi a[j] > x ⇒ j ; o Bước 2-3: Nếu i < j o Nếu i < j: ⇒ Bước 2; ⇒ Swap(a[i], a[j]) // a[i], a[j] sai thứ tự // chưa hết mảng dừng Bước 3: o Nếu i ≥ j: ⇒ Dừng Giải thuật để sắpxếp dãy a[left], a[left+1], , a[right]: được phát... do C.A.R Hoare đề xuất Thuật toán này có tốc độ trung bình nhanh hơn các thuật toán sắpxếp tổng quát khác Do đó Hoare dùng chữ “Quick” để đặt tên cho thuật toán này Ý tưởng chính: Để sắp dãy a[1] a[n], ta thực hiện sắpxếp dãy a từ chỉ số 1 đến chỉ số n QuickSort dựa trên phân hoạch dãy ban đầu thành hai phần dựa vào giá trị x, x là giá trị của một phần tử tùy ý trong dãy ban đầu: Dãy thứ 1: gồm... Hình 4.3: Minh họa đổi chỗ trực tiếp 1.3.5 Phương pháp ShellSort Trong phương pháp sắpxếp kiểu chèn nếu ta luôn phải chèn một khóa vào vị trí đầu dãy thì dẫn đến hạn chế của thuật toán này Để khắc phục trong trường hợp này thì người ta đưa ra một phương pháp sắpxếp là ShellSort Ý tưởng chính: xét một dãy a[1] a[n], cho một số nguyên h (1 ≤ h ≤ n), ta có thể chia dãy đó... Bước 2: Chia dãy ban đầu thành các dãy con có bước nhảy là h[i] Thực hiện sắpxếp từng dãy con bằng phương pháp chèn trực tiếp Bước 3: i = i+1 o Nếu i > k: ⇒ Dừng o Ngược lại: ⇒ Bước 2 Ví dụ: cho dãy bên dưới với n = 8, h = {5, 3, 1} 10 3 7 Ta có minh họa như sau: 6 2 5 4 16 Hình 4.4: Minh hoạ ShellSort Cài đặt ShellSort: sắpxếp dãy a[] tăng, với h[] là mảng chứa các độ dài (bước nhảy) đã chọn sẵn:... 3 2 5 2 7 4 4 16 16 5 Những dãy này được coi là những dãy con xếp theo độ dài bước h Tư tưởng chính của thuật toán ShellSort là: với mỗi bước h, áp dụng thuật toán sắpxếp kiểu chèn từng dãy con độc lập để làm mịn dần các phần tử trong dãy chính Tiếp tục làm tương tự đối với bước (h div 2) cho đến khi h = 1 thì ta được dãy phần tử được sắp Xét trong ví dụ trên, nếu chúng ta dùng phương pháp chèn thì... đó nó phải chèn vào vị trí thứ 1, tức là phải chèn trước 4 phần tử trước nó Nhưng nếu chúng ta xem 2 là phần tử của dãy 2 thì ta chỉ cần chèn trước một phần tử là 3 Đây chính là nguyên nhân thuật toán ShellSort thực hiện hiệu quả hơn sắpxếp chèn Khi đó khóa nhỏ nhanh chóng đưa về gần vị trí đúng của nó Các bước thực hiện chính như sau: Bước 1: chọn k khoảng cách h[1], h[2], , h[k], và i = 1 Bước... khi a[i] . 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. 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