So sánh hiệu năng và ứng dụng thực tế của một số thuật toán sắp xếp

120 0 0
So sánh hiệu năng và ứng dụng thực tế của một số thuật toán sắp xếp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

vì không xét đến các vị trí đã được sắp xếp ở cuối dãy.Dưới đây là mã C++ của hàm bubbleSort:Kết quả sau khi chạy chương trình:Tuy nhiên, với thuật toán sắp xếp truyền thống này, nếu tro

Trang 1

TRƯỜNG ĐẠI HỌC CÔNG NGHIỆP HÀ NỘIKHOA CÔNG NGHỆ THÔNG TIN

Sinh viên: Cao Sỹ Minh Hoàng

Lê Tuấn Hưng Nguyễn Việt Hoàng Cao Thị Thanh Quỳnh Đỗ Ngọc Giang

Lớp: 20222IT6040004 Khóa: 16

Trang 2

4 Phương pháp nghiên cứu 6

CHƯƠNG 1: CƠ SỞ LÝ THUYẾT 7

1 Thuật toán sắp xếp 7

2 Thống kê thời gian thực thi 7

2.1 Khái niệm thời gian thực thi 7

2.2 Phân tích tình huống xấu nhất (worst case) 7

2.3 Phân tích tình huống tốt nhất (best case) 8

2.4 Phân tích tình huống trung bình (average case) 8

3 Sự gia tăng về thời gian thực thi của thuật toán 8

3.1 Khái niệm 8

3.2 Các ký hiệu biểu diễn độ phức tạp của thuật toán 8

3.3 Một vài độ phức tạp thường gặp 9

3.4 Tính toán thời gian chạy 9

4 Phân loại các thuật toán sắp xếp 10

4.1 Phân loại theo độ phức tạp về thời gian 10

4.2 Phân loại theo độ phức tạp về không gian 11

4.3 Phân loại theo tính ổn định của thuật toán 11

4.4 Phân loại thuật toán đệ quy và thuật toán vòng lặp 11

CHƯƠNG 2: KHẢO SÁT CÁC THUẬT TOÁN 12

1 Thuật toán sắp xếp nổi bọt(Bubble Sort) 12

Trang 3

1.1 Ý tưởng thuật toán 12

1.2 Giải thuật Bubble Sort 12

1.3 Mô tả thuật toán 13

1.4 Code tham khảo 13

1.5 Đánh giá về thuật toán Bubble Sort 15

1.5.1 Độ phức tạp về thời gian 15

1.5.2 Độ phức tạp về không gian 16

1.5.3 Độ ổn định 16

1.6 Ứng dụng thực tế của thuật toán Bubble Sort 16

2 Thuật toán sắp xếp chọn(Selection Sort) 16

2.1 Ý tưởng thuật toán 16

2.2 Giải thuật Selection Sort 17

2.3 Mô tả thuật toán 18

2.4 Code tham khảo 19

2.5 Đánh giá về thuật toán Selection Sort 20

2.5.1 Độ phức tạp về thời gian 20

2.5.2 Độ phức tạp về không gian 21

2.5.3 Độ ổn định 21

2.6 Ứng dụng thực tế của thuật toán Selection Sort 21

3 Thuật toán sắp xếp chèn (Insertion Sort) 22

3.1 Ý tưởng thuật toán 22

3.2 Lưu đồ thuật toán 23

3.3 Mô tả thuật toán 23

3.4 Code tham khảo 24

3.5 Đánh giá về thuật toán Insertion Sort 25

3.5.1 Độ phức tạp về thời gian 25

3.5.2 Độ phức tạp về không gian 25

3.5.3 Độ ổn định 26

4 Thuật toán sắp xếp trộn(Merge Sort) 26

4.1 Ý tưởng thuật toán 26

4.2 Giải thuật Merge Sort 26

4.3 Mô tả thuật toán 26

Trang 4

4.4 Code tham khảo 28

4.5 Đánh giá về thuật toán Merge Sort 33

4.5.1 Độ phức tạp về thời gian 33

4.5.2 Độ phức tạp về không gian 33

4.5.3 Độ ổn định 33

4.6 Ứng dụng thực tế của thuật toán Merge Sort 33

5 Thuật toán sắp xếp vun đống (Heap Sort) 34

5.1 Ý tưởng thuật toán 34

5.2 Giải thuật Heap Sort 34

5.3 Mô tả thuật toán 34

5.3.1 Mô tả thuật toán Heap Sort như sau 34

5.3.2 Hình ảnh mô tả thuật toán Heap Sort 35

5.4 Code tham khảo 38

5.5 Đánh giá về thuật toán Heap Sort 40

5.5.1 Độ phức tạp về thời gian 40

5.5.2 Độ phức tạp về không gian 41

5.5.3 Độ ổn định 41

5.6 Ứng dụng thực tế của thuật toán Heap Sort 41

6 Thuật toán sắp xếp nhanh (Quick Sort) 41

6.1 Ý tưởng thuật toán 42

6.2 Mô tả thuật toán 44

6.3 Code tham khảo 47

Trang 5

1.7 Tổng kết 50

2 Phân chia các yếu tố cần xem xét của bộ dữ liệu 50

2.1 Phân loại kiểu dữ liệu 50

2.2 Số lượng phần tử 50

2.3 Thứ tự các phần tử trong mảng 51

3 Một vài điểm lưu ý khi benchmarks 51

3.1 Cấu hình máy benchmarks trong suốt quá trình 51

3.2 Số lần benchmarks 51

3.3 Đơn vị đo: second (s) 51

3.4 Dự đoán thời gian thực thi 51

4 Đo lường thực tế 52

4.1 Đo lường thuật toán Bubble Sort 52

4.2 Đo lường thuật toán Selection Sort 55

4.3 Đo lường thuật toán Insertion Sort 58

4.4 Đo lường thuật toán Merge Sort 61

4.5 Đo lường thuật toán Quick Sort 70

4.6 Đo lường thuật toán Heap sort 79

CHƯƠNG 4: SO SÁNH HIỆU NĂNG CỦA CÁC THUẬT TOÁN 88

1 Phân nhóm thuật toán sắp xếp dựa trên độ phức tạp 88

2 Phương pháp so sánh 88

3 Tiến hành so sánh các thuật toán 89

3.1 Trên bộ dữ liệu chân lý 89

3.2 Trên bộ dữ liệu số nguyên 92

3.3 Trên bộ dữ liệu số thực 95

3.4 Trên bộ dữ liệu sinh viên 98

CHƯƠNG 5: THẢO LUẬN VÀ TÓM TẮT 116

Trang 6

LỜI MỞ ĐẦU

1 Lý do chọn đề tài

Với sự phát triển nhanh chóng của công nghệ, máy tính trở nên phổ biến và gần gũi hơn với con người, mọi việc dần được tương tác thông qua máy tính nhiều hơn Đặc biệt là sau đại dịch Covid 19 đã đẩy nhanh tốc độ chuyển đổi công nghệ số ở rất nhiều quốc gia Do vậy số lượng dữ liệu người dùng tương tác qua máy tính tăng lên, dẫn đến sự quan tâm về phát triển của các thuật toán sắp xếp cũng được tăng lên Điều này thu hút một lượng lớn sự quan tâm từ các nhà nghiên cứu về cách phát triển các thuật toán sắp xếp Họ tìm cách phát triển các thuật toán sắp xếp bằng cách cải thiện hiệu năng và làm giảm độ phức tạp về thuật toán Bất kỳ thay đổi nhỏ nào trong giải thuật sắp xếp cũng có thể dẫn đến sự cải thiện cho chúng hoặc tạo ra những thuật toán mới ánh xạ lại tính ưu việt hơn so với những thuật toán tiền nhiệm.

Vì vậy đề tài “So sánh hiệu năng và ứng dụng thực tế của một số thuật toán sắp xếp” được chọn để nghiên cứu trong học phần Thực tập cơ sở ngành Kỹ thuật phần mềm.

Đề tài “So sánh hiệu năng và ứng dụng thực tế của một số thuật toán sắp xếp” được hoàn thành với sự hướng dẫn trực tiếp của giảng viên: Ths Vũ Thị Dương Qua đây chúng em xin chân thành cảm ơn cô đã giúp chúng em hoàn thành đề tài này.

2 Mục tiêu đề tài

Tìm hiểu về các thuật toán sắp xếp phổ biến như: Sắp xếp nổi bọt (Bubble Sort), Sắp xếp chọn (Selection Sort), Sắp xếp chèn (Insertion sort), Sắp xếp trộn (Merge Sort), Sắp xếp phân đoạn (Quicksort), Sắp xếp vun đống (Heap Sort).

So sánh hiệu năng (thời gian thực thi, độ phức tạp) của các thuật toán trên dựa trên các tiêu chí khác nhau (bộ dữ liệu, độ lớn dữ liệu…).

Trang 7

Đánh giá ứng dụng của các thuật toán trong thực thế một số ngành nghề Đề xuất những cải tiến, tối ưu hóa cho các thuật toán (nếu có).

3 Phạm vi nghiên cứu

Bài tập lớn sẽ tập trung nghiên cứu và phân tích các thuật toán sắp xếp phổ biến đã nêu trên, không bao gồm các biến thể và thuật toán sắp xếp đặc biệt khác.

Tìm hiểu và phân tích các thuật toán sắp xếp phổ biến, bao gồm nhưng không giới hạn là: Bubble Sort, Insertion Sort, Selection Sort, Quicksort, Merge Sort, Heap Sort

Thiết kế và triển khai các thuật toán sắp xếp trên các ngôn ngữ lập trình C++.

Phạm vi nghiên cứu có thể được điều chỉnh hoặc mở rộng phù hợp với yêu cầu và giới hạn của đề tài.

4 Phương pháp nghiên cứu

Nghiên cứu lý thuyết: Tìm hiểu và tổng hợp kiến thức về các thuật toán sắp xếp từ sách, bài giảng, tài liệu trực tuyến, các bài báo nghiên cứu khoa học.

Thiết kế và triển khai các thuật toán sắp xếp: Triển khai mã nguồn của từng thuật toán và thực nghiệm trên các bộ dữ liệu khác nhau (kích thước, độ phức tạp).

Thực hiện thí nghiệm và phân tích kết quả: Thống kê, đánh giá kết quả thực nghiệm, so sánh hiệu năng và tính ứng dụng của từng thuật toán.

Xin ý kiến từ giảng viên: Tham khảo ý kiến của giảng viên hướng dẫn và các chuyên gia trong lĩnh vực để đề xuất cải tiến, tối ưu hóa.

Trang 8

CHƯƠNG 1: CƠ SỞ LÝ THUYẾT

1 Thuật toán sắp xếp

Sắp xếp là một quá trình sắp đặt lại một danh sách các phần tử theo đúng thứ tự bởi vì xử lý các phần tử theo một thứ tự nhất định luôn hiệu quả hơn xử lý các phần tử theo một thứ tự ngẫu nhiên.

Thuật toán sắp xếp là thuật toán dùng để sắp xếp một danh sách các phần tử theo một trật tự hợp lý như theo giá trị, theo bảng chữ cái; theo chiều tăng, giảm dần.

5 Thống kê thời gian thực thi5.1.Khái niệm thời gian thực thi

Thời gian thực thi là quá trình dự đoán và ước tính sự gia tăng về thời gian của thuật toán cùng với sự gia tăng của kích thước dữ liệu đầu vào Một giây máy tính có thể thực hiện xấp xỉ 10^8 phép tính Tức là trong một giây có thể có 10^8 câu lệnh được thực hiện.

Bên cạnh số lượng phần từ của dữ liệu đầu vào, ta cũng cần quan tâm đến số lượng phép toán được thực hiện trong lúc thực thi.

Để đảm bảo được việc làm chủ và kiểm soát một thuật toán, ta thường đoán trước 3 tình huống của nó: tình huống tốt nhất (best case), tình huống xấu nhất (worst case) và tình huống trung bình (average case) Những phân tích dưới đây sẽ giúp bạn hiểu rõ hơn về 3 tình huống này.

5.2.Phân tích tình huống xấu nhất (worst case)

Tình huống xấu nhất ước tính được thời gian chạy lớn nhất mà một chương trình cần để giải quyết một bộ dữ liệu đầu vào cụ thể.

Thời gian thực thi chương trình của tình huống xấu nhất cung cấp chúng ta cận trên của về độ phức tạp tính toán.

Vì là tình huống xấu nhất nên nó cũng đảm bảo hiệu suất của một thuật

Trang 9

Trong lĩnh vực công nghệ thông tin, các nhà phát triển thường quan tâm đến tình huống xấu nhất Giúp bao quát mọi tình huống xảy ra và làm cho chương trình của họ hoạt động luôn luôn ổn định.

5.3.Phân tích tình huống tốt nhất (best case)

Mặt khác, tình huống tốt nhất ước tính được thời gian chạy ngắn/nhanh nhất mà một chương trình cần để giải quyết bộ dữ liệu đầu vào cụ thể.

Thời gian thực thi chương trình của tình huống xấu nhất cung cấp chúng ta cận dưới về độ phức tạp tính toán.

Tình huống này thường rất ít được quan tâm vì phạm vi bao quát hạn chế, không hữu ích và phi thực tế.

5.4.Phân tích tình huống trung bình (average case)

Tính huống trung bình ước tính thời gian chạy trung bình mà một chương trình cần để giải quyết bộ dữ liệu đầu vào cụ thể.

Nó được tính bằng cách lấy trung bình thời gian thực thi của tất cả các tình huống có thể xảy ra đối với thuật toán đó

Hiệu năng với tình huống trung bình thường tính toán khó hơn 2 trường hợp còn lại bởi nó bao quát nhiều biến thể với bộ dữ liệu đầu vào có thể xảy ra Đó cũng là lý do phân tích tình huống xấu nhất trở nên phổ biến.

6 Sự gia tăng về thời gian thực thi của thuật toán6.1.Khái niệm

Sự gia tăng về thời gian thực thi của thuật toán là khái niệm chỉ về gia tăng thời gian thực thi khi kích thước dữ liệu đầu vào tăng lên Nếu lựa chọn thuật toán không tối ưu sẽ ảnh hưởng đến tốc độ xử lý hệ thống, dẫn đến ảnh hưởng đến trải nghiệm của người dùng.

6.2.Các ký hiệu biểu diễn độ phức tạp của thuật toán

Trang 10

Trong khoa học máy tính, chúng ta thường sử dụng một vài kí hiệu để mô tả ước tính thời gian chạy như: ký hiệu O (Big-O), ký hiệu Ω (Omega),

nlogn Loga tuyến tính Sắp xếp trộn, sắp xếp nhanh

chèn, sắp xếp chọn

của dãy fibonacci

6.4.Tính toán thời gian chạy

Để dễ dàng hơn trong việc phân tích và đánh giá hiệu năng thuật toán, ta sử dụng 3 quy tắc sau đây: quy tắc cộng, quy tắc nhân, quy tắc bỏ hằng

Quy tắc cộng: thường xuất hiện trong các vòng lặp rời rạc nhau:

Nếu độ phức tạp của một thuật toán là O(n) + O(n) thì ta có thể coi độ phức tạp của cả thuật toán là O(n)

Nếu độ phức tạp của một thuật toán là O(n) + O(m) thì ta có thể coi độ phức tạp của cả thuật toán là O(m) nếu m > n và ngược lại

Trang 11

Quy tắc nhân: thường xuất hiện trong các vòng lặp lồng nhau:

Nếu vòng lặp ngoài cần lặp n bước, vòng lặp bên trong cần lặp m bước thì ta có độ phức tạp của thuật toán đó là O(m*n), tương tự với vòng lặp bên trong cần lặp n bước thì ta được độ phức tạp của thuật toán đó là O(n*n) = O(n2)

Với việc tăng lên độ phức tạp của thuật toán theo cấp số nhân, nên việc khử đi được 1 vòng lặp bên trong trở nên vô cùng quan trọng và luôn được tìm giải pháp.Nếu thực hiện được thì hiệu năng của thuật toán cũng như của chương trình sẽ tăng lên một cách đáng kể Quy tắc bỏ hằng: thường áp dụng với những vòng lặp mà bên trong chứa các phép toán cố định, không phụ thuộc vào kích thước dữ liệu truyền vào:

Như trong vòng lặp n bước, và luôn thực hiện 2 phép toán cố định không phụ thuộc vào n, thì ta được độ phức tạp của thuật toán là O(2*n) = O(n).

Quy tắc bỏ hằng giúp giải thích được lý do tại sao một số thuật toán chia 2 kích thước bài toán liên tiếp nhưng lại ký hiệu là logn thay vì log2n, vì: log2n = logn/log2; vì log2 là hằng số nên ta loại bỏ, như vậy chỉ còn logn.

7 Phân loại các thuật toán sắp xếp

Về cơ bản, thuật toán sắp xếp có thể được phân loại dựa trên nhiều tiêu chí khác nhau, dưới đây là một vài tiêu chí để phân loại thuật toán sắp xếp.

7.1.Phân loại theo độ phức tạp về thời gian

Phân loại theo độ phức tạp về thời gian hay nói cách khác là phân loại theo số câu lệnh phải thực thi dựa trên giá trị đầu vào Thương được chia thành 2 nhóm chính: O(nlogn) và O(n2).

7.2.Phân loại theo độ phức tạp về không gian

Trang 12

Xét trên khía cạnh phân loại theo độ phức tạp về không gian, ta chia chúng thành 2 chủng loại: sắp xếp tại chỗ và sắp xếp không tại chỗ.

Sắp xếp tại chỗ (in place sorting): sắp xếp dữ liệu trực tiếp trên mảng ban đầu, nên không cần thêm các bộ nhớ phụ Các thuật toán sắp xếp tại chỗ phổ biến như bubble sort, selection sort…

Sắp xếp không tại chỗ (out place sorting): sắp xếp dữ liệu trong mảng mới, được tạo ra từ mảng ban đầu Các thuật toán sắp xếp không tại chỗ phổ biến như: merge sort, counting sort,…

Vào thời kỳ đầu, khi công nghệ còn chưa phát triển, bộ nhớ còn hạn hẹp nên việc tràn bộ nhớ thường xuyên xảy ra, khi ấy các kỹ sư luôn phải đau đầu với các cấu trúc dữ liệu, làm sao để tối ưu bộ nhớ nhất có thể Tuy nhiên, với sự tiến hoá mạnh mẽ của bộ nhớ, thì ngày nay ta có thể lưu trữ hàng terabytes dữ liệu chỉ trong một đối tượng to bằng hai đốt ngón tay Vì vậy, ngày nay, các nhà phát triển thường chú trọng đến việc hy sinh bộ nhớ để cải thiện hiệu năng, sử dụng bộ nhớ để tạo thêm biến giúp việc viết mã, bảo trì, nâng cấp hệ thống trở nên dễ dàng hơn.

7.3.Phân loại theo tính ổn định của thuật toán

Phân loại thuật toán sắp xếp theo tính ổn định là phương pháp phân loại dựa trên tính chất giữ nguyên thứ tự các phần tử có cùng giá trị sau khi sắp xếp.

Thường thì cách phân loại này hữu dụng với các mảng là đối tượng, ta muốn giữ lại thứ tự của chúng

7.4.Phân loại thuật toán đệ quy và thuật toán vòng lặp

Thuật toán đệ quy thường được sử dụng trong chiến lược chia để trị, sẽ chia bài toán thành các bài toán con tương tự nhưng kích thước nhỏ hơn, thường các thuật toán đệ quy sẽ phải xác định được 2 yếu tố: bài toán con nhỏ nhất và công thức truy hồi để tránh gặp tình trạng đệ quy không điểm dừng.

Trang 13

Thuật toán lặp là một hình thức lặp đi lặp lại một tập lệnh cho đến khi điều kiện lặp lại không còn đúng nữa Và vòng lặp có thể giải quyết các vấn đề mà không phải chia thành các vấn đề con.

Tuy nhiên ngày nay với lượng dữ liệu lớn, việc sử dụng phương pháp đệ quy có thể dẫn đến tình trạng tràn stack (stack overflow) nếu quá trình đệ quy diễn ra quá sâu mà không được xử lý đúng cách Điều đó tạo ra thuật ngữ “khử đệ quy” (recursion/iteration), cũng là một cách nói về cách tiếp cận bài toán thông qua vòng lặp.

Nhìn chung thì nếu sử dụng đệ quy thì sẽ giúp lập trình viên dễ hiểu code hơn, còn sử dụng vòng lặp thì sẽ đem lại tốc độ tốt hơn so với đệ quy.

CHƯƠNG 2: KHẢO SÁT CÁC THUẬT TOÁN

1 Thuật toán sắp xếp nổi bọt(Bubble Sort)7.5.Ý tưởng thuật toán

Ta sẽ duyệt từ đầu dãy, trong quá trình duyệt, nếu phần tử đứng ở dưới (đứng phía sau) nhỏ hơn phần tử đứng ngay trên (trước) nó thì theo

nguyên tắc của bọt khí, phần tử nhẹ sẽ bị “trồi” lên phía trước phần tử nặng (hai phần tử này sẽ được đổi chỗ cho nhau) Kết quả là phần tử nhỏ nhất (nhẹ nhất) sẽ được đưa lên (trồi lên) bề mặt (đầu mảng) rất nhanh.

Sau mỗi lần đi ta sẽ đưa được một phần tử trồi lên đúng chỗ Do vậy, sau n - 1 lần đi thì tất cả các phần tử trong mảng sẽ có thứ tự tăng.

7.6.Giải thuật Bubble Sort1 Bắt đầu từ đầu danh sách.

2 So sánh 2 phần tử đầu tiên Nếu phần tử đầu tiên lớn hơn phần tử

thứ hai, hoán đổi vị trí của chúng.

3 Di chuyển xuống phần tử tiếp theo và lặp lại bước 2 cho đến hết

danh sách.

4 Nếu trong quá trình lặp lại các phần tử, không có phần tử nào được

hoán đổi, danh sách đã được sắp xếp và thuật toán có thể kết thúc.

Trang 14

5 Nếu có phần tử được hoán đổi, quay lại bước 1 để tiếp tục lặp lại

toàn bộ danh sách.

6 Trả về dãy số đã sắp xếp.

7.7.Mô tả thuật toán

Mô tả phương pháp sắp xếp nổi bọt:

i = 2 23 11 34 42 58 6574 Tương tự với lần duyệt i = 1

i = 3 11 23 34 42 586574 Tương tự với lần duyệt i = 1

i = 4 11 23 34 42 586574 Tương tự với lần duyệt i = 1

i = 5 11 23 34 42 586574 Tương tự với lần duyệt i = 1

i = 6 1123 34 42 586574 Dãy đã được sắp xếp theo thứ

tự tăng dần

7.8.Code tham khảo

Hàm bubbleSort(): sử dụng hai vòng lặp lồng nhau, với hai tham số

Mảng X[]: Mảng chứa các phần tử cần được sắp xếp.

Trang 15

Chỉ số n: số lượng phần tử của mảng.

Thuật toán bubbleSort() thực hiện như sau:

1 So sánh hai phần tử liền kề nhau, nếu chúng là một cặp nghịch

thế, đổi chỗ luôn.

7. Sau mỗi lần lặp i, ta sẽ có phần tử lớn nhất ở cuối dãy.

8. Ở các lần lặp tiếp theo ta chỉ cần duyệt từ j = 0 đến vị trí n – i,

9. vì không xét đến các vị trí đã được sắp xếp ở cuối dãy.

Dưới đây là mã C++ của hàm bubbleSort():

Kết quả sau khi chạy chương trình:

Tuy nhiên, với thuật toán sắp xếp truyền thống này, nếu trong trường hợp mà các phần tử đã ở đúng vị trí, các mảng vẫn phải chạy tiếp vòng lặp theo đúng thuật toán, điều này sẽ làm tốn thời gian thực hiện.

Để tối ưu hơn, ta sẽ sử dụng thêm một biến phụ “swapped” để đánh dấu Nếu các vị trí được hoán đổi biến “swapped” sẽ đổi thành true, còn

Trang 16

sau vòng lặp j không có phần tử nào hoán đổi (tức là mảng đã được sắp xếp), biến phụ “swapped” sẽ mang giá trị false, và ta sẽ break vòng lặp.

Dưới đây là code tham khảo:

Kết quả sau khi chạy chương trình:

7.9.Đánh giá về thuật toán Bubble Sort7.9.1 Độ phức tạp về thời gian

Độ phức tạp thời gian của thuật toán Bubble Sort là O(n2), trong đó n là số lượng phần tử trong dãy số cần sắp xếp.

Trường hợp xấu nhất: O(n2)

Nếu chúng ta muốn sắp xếp theo thứ tự tăng dần và mảng đã cho theo thứ tự giảm dần thì trường hợp xấu nhất sẽ xảy ra.

Trường hợp tốt nhất: O(n)

Nếu mảng đã được sắp xếp thì không cần sắp xếp.

Trang 17

Trường hợp trung bình: O(n2)

Nó xảy ra khi các phần tử của mảng có thứ tự lộn xộn (không tăng dần cũng không giảm dần).

7.9.2 Độ phức tạp về không gian

Độ phức tạp không gian của thuật toán sắp xếp nổi bọt là O(1) Trong thuật toán này, ta chỉ sử dụng một số hằng để lưu trữ các giá trị tạm thời trong quá trình hoán đổi các phần tử của danh sách Các giá trị này được lưu trữ trong vùng nhớ của biến tạm thời trong khi thuật toán đang thực hiện và bị giải phóng khi thuật toán kết thúc Do đó, không có bộ nhớ bổ sung nào được cấp phát trong quá trình sắp xếp và độ phức tạp không gian của thuật toán sắp xếp nổi bọt là O(1).

7.9.3 Độ ổn định

Bubble Sort là một thuật toán ổn định, nghĩa là nó sẽ giữ nguyên thứ tự của các phần tử có cùng giá trị sau khi sắp xếp.

Trong quá trình hoán đổi hai phần tử nếu chúng có cùng giá trị thứ tự của chúng vẫn được giữ nguyên Vì vậy, độ ổn định của thuật toán không thay đổi trạng thái sắp xếp ban đầu của các phần tử có giá trị bằng nhau.

7.10 Ứng dụng thực tế của thuật toán Bubble SortCác ứng dụng của thuật toán Bubble Sort bao gồm:

Sắp xếp dữ liệu: Như đã nói ở trên, Bubble Sort phù hợp với những danh sách có độ phức tạp thấp.

Sắp xếp dữ liệu trong tập tin hoặc một cơ sở dữ liệu vừa và nhỏ, giúp cho việc tìm kiếm dữ liệu trở nên dễ dàng hơn.

Sắp xếp các sản phẩm trên trang web theo thứ tự tăng dần hoặc giảm dần, từ đó có thể phân loại và quản lý theo giá.

Có thể ứng dụng 1 phần trong việc giải quyết bài toán tìm cực trị hay tính toán ma trận với số lượng ít.

8 Thuật toán sắp xếp chọn(Selection Sort)8.1.Ý tưởng thuật toán

Trang 18

Thuật toán sắp xếp chọn là một thuật toán đơn giản để sắp xếp một danh sách các phần tử Thuật toán này hoạt động bằng cách tìm kiếm phần tử nhỏ nhất trong danh sách và đưa nó vào vị trí đầu tiên của danh sách đã sắp xếp Sau đó thuật toán sẽ tìm kiếm phần tử nhỏ nhất và đưa nó vào vị trí thứ hai của danh sách đã sắp xếp, và tiếp tục như vậy cho đến khi toàn bộ danh sách được sắp xếp.

8.2.Giải thuật Selection Sort

1. Chọn phần tử đầu tiên trong danh sách làm phần tử nhỏ nhất

10.Tìm phần tử nhỏ nhất trong phần tử còn lại của danh sách bằng cách

so sánh từng phần tử với phần tử nhỏ nhất hiện tại.

11.Hoàn đổi phần tử nhỏ nhất vừa tìm được với phần tử đầu tiên của danh sách chưa được sắp xếp.

12.Tiếp tục tìm phần tử nhỏ nhất trong phần tử còn lại của danh sách

(không bao gồm phần tử đã được sắp xếp) và hoán đổi nó với phần tử tiếp theo của danh sách chưa được sắp xếp.

13.Tiếp tục cho đến khi danh sách đã được sắp xếp hoàn toàn.

Trang 19

8.3.Mô tả thuật toán

Hình ảnh mô tả thuật toán Selection Sort

Trang 20

Cấu trúc của thuật toán selection Sort bằng ngôn ngữ C++:

Hình ảnh mô tả cấu trúc thuật toán selection Sort trong C++

8.4.Code tham khảo

Hàm selectionSort (): sử dụng hai vòng lặp lồng nhau để thực hiện

việc sắp xếp, nó sẽ nhận vào 2 tham số:

Mảng a[]: Mảng chứa các phần tử cần được sắp xếp.

16. Sau khi chạy xong vòng lặp j, nếu min khác vị trí i khởi tạo, đổi chỗ a[min] và a[i].

17. Ngược lại nếu min = i, không thực hiện đổi chỗ Tăng i lên 1 và thực hiện đến khi kết thúc vòng lặp i.

Trang 21

Dưới đây là mã C++ của hàm selectionSort():

Kết quả sau khi chạy thuật toán:

8.5.Đánh giá về thuật toán Selection Sort8.5.1 Độ phức tạp về thời gian

Độ phức tạp thời gian của thuật toán Selection Sort là O(n2), trong đó n là số lượng phần tử trong dãy số cần sắp xếp.

Trường hợp xấu nhất: O(n2)

Nếu chúng ta muốn sắp xếp theo thứ tự tăng dần và mảng đã cho theo thứ tự giảm dần thì trường hợp xấu nhất sẽ xảy ra.

Trường hợp tốt nhất: O(n2)

Nó xảy ra khi mảng đã được sắp xếp.

Trang 22

Trường hợp trung bình: O(n2)

Nó xảy ra khi các phần tử của mảng có thứ tự lộn xộn (không tăng dần cũng không giảm dần).

Tương tự như Bubble Sort, đây là một độ phức tạp cao trong số các thuật toán sắp xếp cơ bản Vì vậy, Selection Sort không phải là một sự lựa chọn lý tưởng cho những danh sách lớn hoặc danh sách có độ phức tạp cao.

8.5.2 Độ phức tạp về không gian

Độ phức tạp về không gian của Selection Sort cũng là O(1) vì cũng chỉ sử dụng 1 biến “tmp” để đổi vị trí trong suốt quá trình.

8.5.3 Độ ổn định

Thuật toán sắp xếp chọn là một thuật toán không ổn định.

Một thuật sắp xếp được coi là ổn định nếu như hai phần tử bằng nhau trong danh sách gốc vẫn giữ nguyên thứ tự sau khi được sắp xếp Điều này, có nghĩa là nếu có hai phần tử bằng nhau và phần tử đứng trước có chỉ số nhỏ hơn phần tử đứng sau, thì ở danh sách được sắp xếp, phần tử đứng trước sẽ luôn nắm trước phần tử đứng sau.

Tuy nhiên, trong thuật toán sắp xếp chọn, khi tìm thấy phần tử nhỏ nhất và hoán đổi nó với phần tử đầu tiên trong danh sách, vị trí của phần tử bằng nhau có thể bị thay đổi Vì vậy, thuật toán sắp xếp chọn không ổn định, không đảm bảo tính ổn định cho các phần tử bằng nhau trong danh sách được sắp xếp.

8.6.Ứng dụng thực tế của thuật toán Selection Sort Các ứng dụng của thuật toán Selection Sort bao gồm:

Sắp xếp dữ liệu: Selection Sort thường được sử dụng để sắp xếp một danh sách dữ liệu vừa và nhỏ

Trang 23

Sắp xếp các tập dữ liệu của các website đơn giản như danh sách bài viết, bình luận hoặc người dùng theo thứ tự tăng dần hoặc giảm dần theo các thuộc tính như ngày đăng, độ phổ biến…

Sử dụng như một bước trung gian cho các thuật toán khác: như quicksort, thuật toán lập lịch cho CPU,…

Trong nghiên cứu khoa học: Selection Sort có thể dùng để phân tích thống kê trong các bài báo khoa học.

Dễ sử dụng: dễ tiếp cận và dễ học cho sinh viên khi mới bắt đầu trong lập trình.

9 Thuật toán sắp xếp chèn (Insertion Sort)9.1.Ý tưởng thuật toán

Sắp xếp chèn là một thuật toán sắp xếp dựa theo cách sắp xếp các quân bài khi chơi bài Khi muốn sắp một bộ bài theo trật tự người chơi rút lần lượt từ quân thứ hai, so với các quân đứng trước nó để chèn vào vị trí thích hợp.

Trang 24

9.2.Lưu đồ thuật toán

Lưu đồ thuật toán sắp xếp chèn tăng

Với sắp xếp chèn giảm ta đổi điều kiện: a[j] > tmp thành a[j] < tmp

9.3.Mô tả thuật toán

Để sắp xếp dãy A có n phần tử A = {A0, A1, …, An – 1}, ta thực hiện (n – 1) lần lặp:

Với mỗi lần lặp, dãy được chia thành dãy đích và dãy nguồn Trong đó:

Trang 25

+ Dãy đích: gồm các phần tử từ A[0] đến A[i – 1] (với i = 1, 2, …,

n – 1).

+ Dãy nguồn: gồm các phần tử từ A[i] đến A[n – 1] (với i = 1, 2,

…, n – 1).

Với mỗi lần lặp, lấy A[i] là phần tử đầu dãy nguồn (với i [1, n – 1]), chèn vào vị trí thích hợp trong dãy đích.

Mô tả thuật toán sắp xếp chèn tăng với dãy số cụ thể: 17 22 23 29 37 73 Dãy được sắp xếp theo chiều tăng dần

9.4.Code tham khảo

Hàm insertionSort() sử dụng hai vòng lặp lồng nhau, với hai tham số là:

Mảng a[]: Mảng chứa các phần tử cần được sắp xếp n: là số lượng phần tử của mảng a[].

Trang 26

Mã nguồn C++ của thuật toán sắp xếp chèn tăng.

9.5.Đánh giá về thuật toán Insertion Sort9.5.1 Độ phức tạp về thời gian

Trường hợp xấu nhất: O(n2)

Trường hợp được coi là xấu nhất khi cần sắp xếp theo thứ tự tăng dần nhưng dãy ban đầu có thứ tự giảm dần Khi đó mỗi phần tử cần được so sánh với mỗi phần tử khác, do đó, với mỗi phần tử thứ n có (n – 1) số phép so sánh được thực hiện.

Ta có: tổng số phép so sánh = n * ( n – 1) ~ n2

Trường hợp tốt nhất: O(n)

Trường hợp được coi là tốt nhất khi dãy ban đầu đã được sắp xếp Khi đó chỉ có vòng lặp for chạy Do đó, chỉ có n số phép so sánh Hay độ phức tạp là tuyến tính.

Trường hợp trung bình: O(n2)

Trường hợp được coi là trung bình khi dãy ban đầu có thứ tự lộn xộn (không tăng dần cũng không giảm dần).

* Với n: là số lượng phần tử trong dãy cần sắp xếp.9.5.2 Độ phức tạp về không gian

Trang 27

Độ phức tạp về không gian là O(1) vì chỉ có một biến tmp được sử dụng.

9.5.3 Độ ổn định

Sắp xếp chèn là một thuật toán ổn định vì trong quá trình sắp xếp, chỉ hoán đổi thứ tự của hai phân tử nếu phần tử bên phải nhỏ hơn bên trái (hoặc ngược lại) Do đó, thứ tự của hai phần tử có cùng giá trị sẽ luôn được giữ nguyên.

10.Thuật toán sắp xếp trộn(Merge Sort)10.1.Ý tưởng thuật toán

Thuật toán Merge Sort là một trong những thuật toán sắp xếp cơ bản nhất trong khoa học máy tính Ý tưởng của thuật toán sắp xếp Merge Sort là chia dãy số cần sắp xếp thành các phần tử nhỏ hơn, sau đó sắp xếp các phần tử đó và trộn chúng lại thành một dãy số lớn hơn, lặp lại quá trình này cho đến khi dãy số được sắp xếp hoàn toàn.

10.2.Giải thuật Merge Sort

Giải thuật Merge Sort sử dụng phương pháp chia để trị (divide and conquer) để sắp xếp dãy số Cụ thể, giải thuật được thực hiện bằng cách chia dãy số cần sắp xếp thành hai phần bằng cách tìm chỉ số trung tâm Sau đó, tiếp tục chia đôi các phần này cho đến khi mỗi phần chỉ còn một phần tử.

Tiếp theo, các phần được trộn (merge) lại với nhau bằng cách so sánh các phần tử trong từng phần và đưa chúng vào một dãy mới theo thứ tự tăng dần Quá trình trộn này được thực hiện cho tất cả các phần tử trong hai phần đã chia ra Sau đó, quá trình được lặp lại cho đến khi dãy số ban đầu được sắp xếp hoàn toàn.

10.3.Mô tả thuật toán

Mô tả thuật toán Merge Sort như sau:

1 Nếu dãy số chỉ còn một phần tử, trả về dãy số đó.

Trang 28

18.Chia dãy số ban đầu thành hai phần bằng cách tìm chỉ số trung tâm.19.Đệ quy sắp xếp các phần tử trong phần đầu tiên.

20.Đệ quy sắp xếp các phần tử trong phần thứ hai.

21.Trộn hai phần đã sắp xếp lại với nhau bằng cách so sánh các phần tử

trong từng phần và đưa chúng vào một dãy mới theo thứ tự tăng dần.

22.Trả về dãy số đã sắp xếp.

Hình ảnh mô tả thuật toán Merge Sort

Cấu trúc của thuật toán Merge Sort bằng ngôn ngữ C++:

Hình ảnh mô tả cấu trúc thuật toán Merge Sort trong C++

Trang 29

Trong đó Hàm merge() sẽ trộn hai mảng con đã được sắp xếp thành một mảng duy nhất Hàm mergeSort() sẽ chia thành hai mảng con và gọi đệ quy cho mỗi mảng con đó để sắp xếp chúng Sau đó, hàm merge() sẽ được gọi để trộn hai mảng con đã sắp xếp thành một mảng duy nhất.

10.4.Code tham khảo

Hàm merge(): được sử dụng để trộn hai mảng con đã sắp xếp

arr[left mid] và arr[mid+1 right] thành một mảng con duy nhất đã sắp xếp, nó nhận vào 4 tham số đó là:

Mảng arr[]: Mảng chứa các phần tử cần được sắp xếp.

Hai chỉ số left và right: Chỉ số của hai đầu mút của mảng arr[] Chỉ số mid: Chỉ số của phần tử ở giữa mảng, nó là điểm phân chia để chia mảng thành hai mảng con.

Thuật toán merge() thực hiện như sau:

1 Khởi tạo biến con trỏ i và j để theo dõi chỉ số của hai mảng

arr[left mid] và arr[mid+1 right].

23 Khởi tạo biến con trỏ k để theo dõi chỉ số của mảng tạm thời.24 So sánh các phần tử arr[i] và arr[j] từ hai mảng con, đưa phần tử

nhỏ hơn vào mảng tạm thời.

25 Tăng chỉ số i hoặc j cho đến khi đến cuối mảng con.

26 Nếu một trong hai mảng con vẫn còn phần tử, chúng ta sẽ sao

chép tất cả các phần tử còn lại vào mảng tạm thời.

27 Sao chép tất cả các phần tử trong mảng tạm thời vào mảng arr[].

Trang 30

Dưới đây là mã C++ của hàm merge():

Trang 31

Hàm mergeSort(): sử dụng phương pháp đệ quy để sắp xếp mảng arr[]

bằng cách chia nó thành hai mảng con, sắp xếp chúng và trộn lại thành một mảng duy nhất, nó sẽ nhận vào 3 tham số:

Mảng arr[]: Mảng chứa các phần tử cần được sắp xếp.

Hai chỉ số left và right: Chỉ số của hai đầu mút của mảng arr[].

Thuật toán mergeSort() thực hiện như sau:

1 Nếu chỉ số left < right, chia mảng arr[] thành hai mảng con 28.arr[left mid] và arr[mid+1 right].

29.Gọi đệ quy hàm mergeSort() cho mảng con bên trái arr[left mid].30.Gọi đệ quy hàm mergeSort() cho mảng con bên phải

Trang 32

Tổng quát chương demo:

Trang 33

Kết quả sau khi chạy chương trình:

Link demo online: https://onlinegdb.com/c2T4bdvUW

Trang 34

10.5.Đánh giá về thuật toán Merge Sort10.5.1.Độ phức tạp về thời gian

Độ phức tạp thời gian của thuật toán Merge Sort là O(n log n), trong đó n là số lượng phần tử trong dãy số cần sắp xếp Độ phức tạp này là khá tốt so với nhiều thuật toán sắp xếp khác và làm cho Merge Sort được sử dụng rộng rãi trong các ứng dụng thực tế.

10.5.2.Độ phức tạp về không gian

Độ phức tạp về không gian của Merge Sort là O(n), trong đó n là số lượng phần tử trong dãy số cần sắp xếp Điều này có nghĩa là thuật toán Merge Sort cần một lượng bộ nhớ phụ trợ để lưu trữ dãy số đang được sắp xếp và các dãy số con được tạo ra trong quá trình sắp xếp Tuy nhiên, độ phức tạp không gian này không phụ thuộc vào việc sắp xếp theo thứ tự tăng dần hay giảm dần.

10.5.3.Độ ổn định

Merge Sort là một thuật toán ổn định, có nghĩa là nếu có hai phần tử bằng nhau trong dãy số ban đầu, thì vị trí của chúng sẽ không bị thay đổi sau khi sắp xếp xong Việc này được đảm bảo bởi quá trình trộn các phần tử trong quá trình thực hiện Merge Sort Vì vậy, nó là một thuật toán rất hữu ích trong các trường hợp mà việc giữ nguyên thứ tự ban đầu của các phần tử trong dãy là quan trọng.

10.6.Ứng dụng thực tế của thuật toán Merge SortCác ứng dụng của thuật toán Merge Sort bao gồm:

Sắp xếp dữ liệu: Merge Sort thường được sử dụng để sắp xếp một danh sách dữ liệu lớn.

Tìm kiếm: Merge Sort cũng được sử dụng để tìm kiếm các phần tử trong danh sách dữ liệu đã sắp xếp.

Trộn dữ liệu: Merge Sort được sử dụng để trộn các danh sách dữ liệu khác nhau.

Xử lý tín hiệu: Merge Sort được sử dụng trong xử lý tín hiệu kỹ thuật số để trộn các tín hiệu âm thanh và hình ảnh.

Trang 35

Chuyển đổi số thập phân sang nhị phân: Merge Sort được sử dụng để chuyển đổi số thập phân sang nhị phân trong các hệ thống máy tính.

Tính toán khoảng cách: Merge Sort cũng được sử dụng để tính toán khoảng cách giữa các đối tượng trong không gian nhiều chiều.

11.Thuật toán sắp xếp vun đống (Heap Sort)11.1.Ý tưởng thuật toán

Thuật toán heapSort là sử dụng cấu trúc dữ liệu heap để sắp xếp một dãy số Heap là một cấu trúc dữ liệu gồm n phần tử có tính chất: phần tử cha lớn hơn hoặc bằng con, phần tử con trái luôn nhỏ hơn phần tử con phải Trong heapSort, ta sẽ xây dựng một heap từ dãy số ban đầu, sau đó lấy ra phần tử lớn nhất của heap và đưa vào vị trí cuối cùng của dãy số chưa được sắp xếp Tiếp tục xây dựng lại heap từ dãy số còn lại, lặp lại quá trình trên cho đến khi dãy số được sắp xếp hoàn chỉnh.

11.2.Giải thuật Heap Sort

Thuật toán heapSort sử dụng cấu trúc dữ liệu heap để thực hiện việc sắp xếp Heap là một cây nhị phân đầy đủ, trong đó giá trị của mỗi nút luôn lớn hơn (hoặc nhỏ hơn) giá trị của các nút con của nó.

Thuật toán heapSort bắt đầu bằng việc chuyển dữ liệu đầu vào thành một heap (max-heap hoặc min-heap tùy thuật toán) Sau đó, nó lấy phần tử lớn nhất (hoặc nhỏ nhất) từ heap và đưa vào danh sách sắp xếp Tiếp theo, nó xóa phần tử đó khỏi heap và tiếp tục đưa ra phần tử lớn nhất (hoặc nhỏ nhất) cho đến khi heap trống.

Trong quá trình xây dựng heap, thuật toán heapSort sẽ thực hiện cập nhật heap sau mỗi lần thêm phần tử vào Cập nhật này được thực hiện bằng cách sắp xếp lại cây nhị phân (đổi chỗ nút cha với nút con) để đảm bảo tính chất heap.

11.3.Mô tả thuật toán

11.3.1.Mô tả thuật toán Heap Sort như sau

Trang 36

1. Chuyển dữ liệu đầu vào thành một heap (max-heap hoặc min-heap).

32. Lấy phần tử lớn nhất (hoặc nhỏ nhất) từ heap và đưa vào danh sách sắp xếp.

33. Xóa phần tử đó khỏi heap.

34. Cập nhật heap sau mỗi lần xóa phần tử.

35. Kết thúc khi dãy số đã sắp xếp tăng dần (hoặc giảm dần).

11.3.2.Hình ảnh mô tả thuật toán Heap Sort

Trang 39

11.4.Code tham khảo

Hàm heapify(): được sử dụng để tái cấu trúc một cây nhị phân thành một

heap Cụ thể, hàm này sắp xếp các phần tử trong cây nhị phân sao cho các phần tử ở mức thấp hơn có giá trị nhỏ hơn hoặc bằng các phần tử ở mức cao hơn, đảm bảo rằng phần tử lớn nhất trong heap luôn nằm ở gốc của cây.

Thuật toán heapify() thực hiện như sau:

1 heapify(arr, n, i) được sử dụng để xây dựng một heap tại node i.36.Hai vòng lặp for đầu tiên được sử dụng để xây dựng một heap đầu

tiên từ mảng đầu vào bằng cách gọi heapify cho từng node của heap.

37.Sau đó, vòng lặp for thứ ba được sử dụng để lặp lại quá trình đưa

phần tử lớn nhất (root) của heap về cuối mảng và tiếp tục heapify mảng còn lại.

Dưới đây là mã C++ của hàm heapify():

Hàm heapSort(): được sử dụng để chuyển mảng vào một heap bằng hàm

heapify() Lấy phần tử nhỏ nhất (ở đỉnh heap), đưa vào đúng vị trí trong

Trang 40

mảng đã sắp xếp và xóa phần tử này khỏi heap Lặp lại cho đến khi heap trống Mảng đã sắp xếp được lưu trữ trong chính mảng đầu vào.

Thuật toán heapSort() thực hiện như sau:

1 Chuyển mảng vào một heap bằng hàm heapifySort.

38.Lấy phần tử nhỏ nhất (ở đỉnh heap), đưa vào đúng vị trí trong mảng

đã sắp xếp và xóa phần tử này khỏi heap Lặp lại cho đến khi heap trống.

39.Mảng đã sắp xếp được lưu trữ trong chính mảng đầu vào.

Dưới đây là mã C++ của hàm heapSort():

Ngày đăng: 29/03/2024, 22:21

Tài liệu cùng người dùng

Tài liệu liên quan