Thuâ ̣t toán Thuật toán , còn gọi là giải thuật, là một tập hợp hữu hạn hay một dãy các quy tắc chặt chẽ của các chỉ thị, phương cách hay 1 trình tự các thao tác trên một đối tượng cụ
Trang 1LỜI MỞ ĐẦU
Ngày nay với sự phát triển nhảy vọt của khoa học vông nghệ nói chung của ngành tin học nói riêng,với những tính năng ưu việt, sự tiện dụng và ứng dụng rộng rãi, tin học ngày nay là một phần không thể thiếu được của nhiều ngành trong công cuộc xây dựng và phát triển xã hội Hơn thế nữa nó còn đi sâu đời sống của con người Tin học đã thâm nhập khá mạnh mẽ vào Việt Nam, nhiều lĩnh vực hoạt động từ lĩnh vực quản lý hành chính, quản lý kinh tế, tự động hóa công nghiệp …đến các lĩnh vực giáo dục và đào tạo đều có thay đổi đáng kể nhờ ứng dụng tin học Máy tính là công cụ cần thiết đối với con người trong thời đại ngày nay
Trong các môn học Toán – Tin, đặc biệt là ngành Công nghệ thông tin, thuật toán đóng vai trò vô cùng quan trọng Việc nắm vững thuật toán giúp ta tìm ra hướng giải quyết vấn đề nhanh hơn, viết code mạch lạc hơn Nắm vững thuật toán, cấu trúc dữ liệu, ta sẽ ước tính được độ phức tạp của code, đánh giá code chạy nhanh hay chậm, có bền vững hay không
Đây là những kĩ năng vô cùng cần thiết để trở thành một kĩ sư giỏi Vì vậy khoa Tin rất tập trung trong việc giảng dạy môn Phân tích và Thiết kế giải thuật Bên cạnh
đó, sinh viên em còn được thực hành và kiểm tra trình độ của mình qua việc làm Đồ án Giải thuật & Lập trình với các đề tài cụ thể và thiết thực
Mặc dù em đã rất cố gắng và nổ lực để làm đồ án này do kinh nghiệm còn hạn chế và kiến thức em nắm chưa sâu nên em biết sẽ không tránh khỏi những thiếu sót
Em rất mong nhận được sự thông cảm và đóng góp của các Thầy, Cô để lần sau làm
đồ án được tốt hơn
Hoàn thành đồ án Giải thuật & Lập trình này là niềm vui của em, em rất là biết
ơn Thầy Hồ Ngọc Tú đã hướng dẫn chúng em tận tình trong suốt thời gian chúng em làm đồ án Một lần nữa nhóm chúng em xin gửi lời cám ơn chân thành nhất đến Thầy
Trang 2MỤC LỤC
LỜI MỞ ĐẦU 2
MỤC LỤC 3
DANH MỤC HÌNH VẼ 4
1 GIỚI THIỆU ĐỀ TÀI 5
2 CƠ SỞ LÝ THUYẾT 5
2.1 Thuật toán 5
2.2 Đô ̣ phức ta ̣p thuâ ̣t toán 6
2.3 Thuật toán sắp xếp 6
2.4 Giải thuâ ̣t chia để tri ̣ (Devide and Conquer) 7
3 TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN 7
3.1 Phát biểu bài toán 7
3.2 Cấu trúc dữ liệu 8
3.3 Thuật toán 8
3.3.1 Sắp xếp cho ̣n (Selection Sort) 8
3.3.2 Sắp xếp chèn (Insertion Sort) 11
3.3.3 Sắp xếp nổi bo ̣t (Bubble Sort) 14
3.3.4 Sắp xếp trô ̣n (Merge Sort) 17
3.3.5 Sắp xếp nhanh (Quick Sort) 21
4 CHƯƠNG TRÌNH VÀ KẾT QUẢ 24
4.1 Tổ chức chương trình 24
4.2 Ngôn ngữ cài đặt 27
4.3 Kết quả 28
4.3.1 Giao diện chính của chương trình 28
4.3.2 Kết quả thực thi của chương trình 29
4.3.3 Nhận xét 31
5 KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN 31
Trang 3DANH MỤC HÌNH VẼ
Trang 41 GIỚI THIỆU ĐỀ TÀI
Hiện nay, có rất nhiều vấn đề phổ biến trong các lĩnh vực thực tiễn về khoa học máy tính, ứng dụng cơ sở dữ liệu, mạng và trí tuệ nhân tạo Một trong những vấn đề cơ bản nhất đó là thuật toán sắp xếp, mô ̣t vấn đề đã thu hút rất nhiều nghiên cứu
Quá trình sắp xếp là quá trình bố trí lại các phần tử theo mô ̣t trâ ̣t tự nhất định để cho việc xử lý trở nên dễ dàng và hiê ̣u quả hơn so với xử lý các phần tử ngẫu nhiên Sắp xếp là một trong những giải thuâ ̣t lập trình phổ biến nhất, ví dụ khi lấy cơ sở dữ liệu ứng dụng, nếu chúng ta muốn quản lý thông tin để thuâ ̣n tiê ̣n cho viê ̣c tìm kiếm, chú ng ta phải giữ thông tin theo thứ tự hợp lý, ví dụ: thứ tự chữ cái theo tên, thứ tự tăng dần hoă ̣c giảm dần theo mã ID, lương, ngày nhâ ̣n vào làm viê ̣c,
Sự tăng trưởng thông tin mô ̣t cách nhanh chóng đã kéo theo sự phát triển của các thuật toán sắp xếp Các thuâ ̣t toán này được phát triển nhằm nâng cao hiê ̣u suất và giảm thiểu đô ̣ phức ta ̣p Dữ liê ̣u có thể xuất hiê ̣n dưới nhiều dạng khác nhau và thường phải được lưu trữ mô ̣t khối lượng đáng kể, nên viê ̣c xây dựng các thuâ ̣t toán sắp xếp cho phép tìm kiếm nhanh đem lại ý nghĩa rất lớn Hiê ̣n nay, có rất nhiều thuâ ̣t toán sắp
xếp được ra đời như sắp xếp chọn, sắp xếp nổi bọt, sắp xếp chèn, sắp xếp trô ̣n, sắp xếp nhanh, mỗi thuật toán đều có một cơ chế khác nhau nhằm làm tăng hiệu suất và hiệu quả của các ứng dụng thực tế và giảm độ phức tạp thời gian của từng ứng dụng
Khi so sánh giữa các thuật toán sắp xếp khác nhau, có ba yếu tố cần được xem xét: độ phức tạp thời gian, sự ổn định và không gian bộ nhớ Qua viê ̣c so sánh, chúng
ta có thể hiểu rõ hơn về ưu điểm, khuyết điểm của các thuâ ̣t toán sắp xếp, từ đó vâ ̣n
dụng đúng thuâ ̣t toán theo yêu cầu của bài toán đă ̣t ra mô ̣t cách hiê ̣u quả và đó cũng là
mục đích của đề tài này
2 CƠ SỞ LÝ THUYẾT
2.1 Thuâ ̣t toán
Thuật toán , còn gọi là giải thuật, là một tập hợp hữu hạn hay một dãy các quy tắc chặt chẽ của các chỉ thị, phương cách hay 1 trình tự các thao tác trên một đối tượng cụ thể được xác định và định nghĩa rõ ràng cho việc hoàn tất một số sự việc từ một trạng thái ban đầu cho trước; khi các chỉ thị này được áp dụng triệt để thì sẽ dẫn đến kết quả sau cùng như đã dự đoán trước
Nói cách khác, thuật toán là một bộ các quy tắc hay quy trình cụ thể nhằm giải quyết một vấn đề nào đó trong một số bước hữu hạn, hoặc nhằm cung cấp một kết quả
Trang 5Một thuật toán có các tính chất sau:
- Tính chính xác: để đảm bảo kết quả tính toán hay các thao tác mà máy tính thực hiện được là chính xác
- Tính rõ ràng: Thuật toán phải được thể hiện bằng các câu lệnh minh bạch; các câu lệnh được sắp xếp theo thứ tự nhất định
- Tính khách quan: Một thuật toán dù được viết bởi nhiều người trên nhiều máy tính vẫn phải cho kết quả như nhau
- Tính phổ dụng: Thuật toán không chỉ áp dụng cho một bài toán nhất định mà
có thể áp dụng cho một lớp các bài toán có đầu vào tương tự nhau
- Tính kết thúc: Thuật toán phải gồm một số hữu hạn các bước tính toán
2.2 Độ phư ́ c ta ̣p thuâ ̣t toán
Độ phức tạp của một thuật toán là 1 hàm phụ thuộc vào độ lớn của dữ liệu đầu vào Để ước lượng độ phức tạp của một thuật toán ta thường dùng khái niệm bậc O-lớn
và bậc Θ
Giả sử f(n) và g(n) là các hàm thực không âm của đối số nguyên không âm n Ta nói “g(n) là O của f(n)” và viết là: g(n) = O(f(n)) nếu tồn tại các hằng số dương C và n0 sao cho g(n) <= C * f(n) với mọi n >= n0
Việc xác định độ phức tạp tính toán của giải thuật trong thực tế có thể tính bằng một số quy tắc đơn giản sau:
- Quy tắc bỏ hằng số:
T(n) = O(c.f(n)) = O(f(n)) với c là một hằng số dương
- Quy tắc lấy max:
2.3 Thuâ ̣t toán sắp xếp
Sắp xếp là quá trình biến đổi mô ̣t danh sách các phần từ thành mô ̣t danh sách thỏa mãn mô ̣t thứ tự xác đi ̣nh nào đó, nhằm có lợi cho việc quản lý và đi ̣nh vi ̣ thông tin Sắp xếp đóng vai trò vô cùng quan tro ̣ng tìm kiếm dữ liê ̣u Chẳng ha ̣n, khi đã có một danh sách được sắp xếp theo thứ tự tăng dần (hoă ̣c giảm dần), ta có thể sử du ̣ng thuật toán tìm kiếm nhi ̣ phân, hiê ̣u quả hơn nhiều so với tìm kiếm tuần tự Trên thực tế, nhiều thuật toán được đưa ra dựa trên ý tưởng xử lý các đối tượng theo mô ̣t thứ tự xác
Trang 6external sorting, chỉ một phần nhỏ dữ liệu cần sắp xếp được đưa vào bộ nhớ trong, phần lớn dữ liệu còn lại được lưu ở bộ nhớ ngoài, kích thước dữ liệu cần sắp xếp lúc này rất lớn, và thời gian sắp xếp thực hiện rất chậm [2]
Các thuật toán sắp xếp được phát triển để sắp xếp dữ liệu theo nhiều cách khác nhau Ví dụ, một mảng các số nguyên có thể được sắp xếp theo thứ tự từ thấp nhất đến cao nhất hoặc từ cao nhất đến thấp nhất, hoặc mảng các phần tử chuỗi có thể được sắp xếp theo thứ tự bảng chữ cái Hầu hết các thuật toán sắp xếp, như sắp xếp lựa chọn, sắp xếp nổi bo ̣t sử dụng kỹ thuật hoán đổi các phần tử cho đến khi đạt được mục tiêu Mỗi thuật toán sắp xếp được chọn dựa trên hiệu quả trường hợp tốt nhất, trường hợp trung bình và trường hợp xấu nhất
2.4 Giải thuâ ̣t chia để tri ̣ (Devide and Conquer)
Phương pháp chia để trị (Divide and Conquer) là một phương pháp quan trọng trong việc thiết kế các giải thuật Ý tưởng của phương pháp này khá đơn giản và rất dễ hiểu: Khi cần giải quyết một bài toán, ta sẽ tiến hành chia bài toán đó thành các bài toán con nhỏ hơn Tiếp tục chia cho đến khi các bài toán nhỏ này không thể chia thêm nữa, khi đó ta sẽ giải quyết các bài toán nhỏ nhất này và cuối cùng kết hợp giải pháp của tất cả các bài toán nhỏ để tìm ra giải pháp của bài toán ban đầu
Giải thuâ ̣t chia để trị bao gồm 3 tiến trình:
- Chia nhỏ:
Trong bước này, chúng ta chia bài toán ban đầu thành các bài toán con Mỗi bài toán con nên là một phần của bài toán ban đầu Nói chung, bước này sử dụng phương pháp đệ qui để chia nhỏ các bài toán cho đến khi không thể chia thêm nữa Khi đó, các bài toán con được gọi là "atomic – nguyên tử", nhưng chúng vẫn biểu diễn một phần nào đó của bài toán ban đầu
- Giải bài toán con:
Trong bước này, các bài toán con được giải
- Kết hợp lời giải:
Sau khi các bài toán con đã được giải, trong bước này chúng ta sẽ kết hợp chúng một cách đệ qui để tìm ra giải pháp cho bài toán ban đầu [3]
3 TỔ CHỨC CẤU TRÚC DỮ LIỆU VÀ THUẬT TOÁN
3.1 Phát biểu bài toán
Đầu vào (Input): tệp chứa mảng các phần tử số nguyên
Trang 73.2 Cấu trúc dữ liệu
- Sử du ̣ng mảng để triển khai giải thuâ ̣t
- int array[]; // chứ a các phần tử của dãy số cần được sắp xếp
- int n; // số phần tử của mảng
đó Cụ thể, duyệt mảng A ban đầu gồm n phần tử A[0], A[1], …, A[n-1] Chọn ra phần
tử nhỏ nhất và sau đó đưa về vị trí đầu tiên của mảng hiện hành Vậy ta đã có được một phần tử đầu tiên A[0] đã được sắp xếp theo thứ tự, loại phần tử đó ra khỏi mảng đang xét
Tiếp tục duyệt mảng gồm n-1 phần tử, bắt đầu từ vị trí A[1] Chọn ra phần tử nhỏ nhất và đưa về vị trí đầu tiên của mảng hiện hành, sau đó loại phần tử đó ra khỏi mảng đang xét Vậy ta đã có được 2 phần tử đầu tiên A[0], A[1] đã được sắp xếp theo thứ tự
Tổng quát, ta duyệt mảng gồm n-i phần tử, bắt đầu từ A[i] (i = 0 n-2), chọn
ra phần tử nhỏ nhất rồi đưa về vị trí A[i]
Cuối cùng ta sẽ thu được mảng đã được sắp xếp theo thứ tự
Bước 3: Hoán vị A[min] với A[i];
Bước 4: i=i+1 Nếu i < n-1, quay lại bước 2; Ngược lại: kết thúc
Trang 83.3.1.3 Minh ho ̣a thuâ ̣t toán
Tiếp tục thực hiện như vậy trong khi i < n-1
Trang 93.3.1.4 Sơ đồ khối
Trang 10- Trườ ng hợp trung bình: tương tự trường hợp xấu nhất, đô ̣ phức ta ̣p là O(n2)
- Trườ ng hợp tốt nhất: đô ̣ phức ta ̣p là O(n2)
3.3.2 Sắp xếp che ̀n (Insertion Sort)
3.3.2.1 Ý tươ ̉ ng
Phương pháp sắp xếp chèn thực hiện bằng cách chèn phần tử đang xét vào vị trí thích hợp trong mảng đã được sắp xếp phía trước nó
Cụ thể, ban đầu, xem phần tử A[0] là mảng đã có thứ tự
Sau đó chèn phần tử A[1] vào đúng vị trí trong mảng gồm A[0] trên sao cho mảng A[0], A[1] được sắp xếp theo thứ tự
Tiếp tục chèn phần tử A[2] vào đúng vị trí trong mảng gồm A[0], A[1] trên sao cho mảng A[0], A[1], A[2] được sắp xếp theo thứ tự
Tổng quát, ta thực hiện chèn phần tử A[i] vào mảng gồm A[0], A[1], …, A[i-1] sao cho mảng A[0], A[1], …, A[i] (i = 1 n-1) được sắp xếp theo thứ tự
Cuối cùng ta sẽ thu được mảng đã được sắp xếp theo thứ tự
3.3.2.2 Thuâ ̣t toán
Input: Mảng A[] gồm n phần tử
Output: Mảng A[] gồm n phần tử đã được sắp xếp
Bước 1: i = 1;
Bước 2: Gán key = A[i];
Bước 3: Tìm vị trí thích hợp trong mảng A[0], …, A[i-1] để chèn A[i] vào;
Bước 4: Chèn key vào vị trí vừa tìm được;
Bước 5: i=i+1 Nếu i < n, quay lại bước 2; Ngược lại: kết thúc
Trang 113.3.2.3 Minh ho ̣a thuâ ̣t toán
Tiếp tục thực hiện như vậy trong khi i < n
Trang 123.3.2.4 Sơ đồ khối
Trang 133.3.2.5 Đô ̣ phức ta ̣p
- Trườ ng hợp xấu nhất: với mỗi i thì số lần so sánh tối đa là i
Độ phức tạp thuật toán là: 𝐓(𝐧) = ∑𝒏−𝟏𝒊=𝟏 𝐢 = 𝒏(𝒏−𝟏)
𝟐 = O(n2)
- Trườ ng hợp trung bình: tương tự trường hợp xấu nhất, đô ̣ phức ta ̣p là O(n2)
- Trườ ng hợp tốt nhất: đô ̣ phức ta ̣p là O(n)
3.3.3 Sắp xếp nổi bo ̣t (Bubble Sort)
3.3.3.1 Ý tươ ̉ ng
Phương pháp sắp xếp nổi bọt thực hiện đúng như tên gọi của nó bằng cách cho
“nổi bọt” những phần tử nhỏ hơn lên đầu mảng
Cụ thể, duyệt từ cuối về đầu mảng, lần lượt xét 2 phần tử kề nhau, nếu phần tử phía sau nhỏ hơn thì sẽ cho “nổi bọt lên”, đổi chỗ với phần tử kề trước nó Kết thúc lần xét duyệt đầu tiên, ta sẽ thu được A[0] là phần tử nhỏ nhất của mảng, loại ra khỏi mảng đang xét
Tiếp tục duyệt từ cuối về đầu mảng hiện hành, sau quá trình “nổi bọt” ta tiếp tục thu được A[1] là phần tử nhỏ nhất tiếp theo, loại ra khỏi mảng đang xét
Tổng quát, ta duyệt từ cuối về đầu mảng gồm n-i phần tử (i = 0 n-1) Lần lượt so sánh 2 phần tử kề nhau và “nổi bọt” lên phần tử nhỏ hơn Sau vòng duyệt thứ i
ta sẽ thu được phần tử thứ A[i] của mảng đã được sắp xếp
Bước 3: Nếu A[j] < A[j-1], đổi chỗ A[j] và A[j-1];
Bước 4: j=j-1 Nếu j > i, quay lại bước 3 Ngược lại: sang bước 5;
Bước 5: i=i+1 Nếu i < n-1, quay lại bước 2; Ngược lại: kết thúc
Trang 143.3.3.3 Minh ho ̣a thuâ ̣t toán
Trang 153.3.3.4 Sơ đồ khối
Trang 16- Trườ ng hợp trung bình: tương tự trường hợp xấu nhất, đô ̣ phức ta ̣p là O(n2)
- Trườ ng hợp tốt nhất: đô ̣ phức ta ̣p là O(n)
3.3.4 Sắp xếp trô ̣n (Merge Sort)
3.3.4.1 Ý tươ ̉ ng
Sắp xếp trộn là một thuật toán được thiết kế bằng kĩ thuật chia để trị Để sắp xếp mảng ban đầu A gồm n phần tử A[0], A[1], …, A[n-1], ta sẽ chia mảng cần xếp thành 2 mảng con phân cách nhau bởi phần tử chính giữa mảng Sau khi thu được 2 mảng con, ta sẽ tiến hành sắp xếp từng mảng con theo thứ tự đúng rồi ghép 2 mảng con lại với nhau, tạo thành mảng A gồm n phần tử ban đầu Trong quá trình sắp xếp từng mảng con, ta lại tiến hành chia đôi, sắp xếp rồi ghép lại Quá trình đó cứ lần lượt được lặp lại cho đến mảng con nhỏ nhất chỉ còn 1 phần tử Sau đó ta so sánh 2 mảng con trong cùng mảng cơ sở (khi chia đôi 1 mảng lớn thành 2 mảng con, mảng lớn đó được gọi là mảng cơ sở), sắp xếp và ghép 2 mảng con đó lại thành mảng cơ sở Việc
so sánh và ghép các mảng con được lặp lại đến khi còn lại mảng cơ sở duy nhất thì đó
là mảng đã được sắp xếp
Trong phương pháp này, các mảng con sau khi được phân hoạch sẽ tiến hành sắp xếp và ghép lại thành mảng cơ sở theo quy trình sau: ta xét 2 phần tử đầu tiên của từng mảng, phần tử nào nhỏ hơn sẽ được đưa vào mảng cơ sở và bị loại khỏi mảng con đang xét Tiếp tục xét 2 phần tử đầu tiên của 2 mảng con đó, chọn ra phần tử nhỏ hơn rồi đưa vào mảng cơ sở Quá trình này được lặp lại cho đến khi 1 trong 2 mảng con trở thành mảng rỗng các phần tử còn lại của mảng con còn lại sẽ được đưa hết vào mảng
cơ sở Cuối cùng, ta sẽ thu được 1 mảng cơ sở có số phần tử bằng 2 mảng con cộng lại
và đã được sắp xếp theo thứ tự đúng
3.3.4.2 Thuâ ̣t toán
Input: Mảng A[] gồm n phần tử với vị trí bắt đầu - l, vị trí kết thúc - r
Output: Mảng A[] gồm n phần tử đã được sắp xếp
Bước 1: Kiểm tra l < r? Trong đó, l là vị trí bắt đầu, r là vị trí kết thúc của mảng đang xét