1 .GIỚI THIỆU
3. MỘT SỐ THUẬT TOÁN SẮP XẾP
3.2 SẮP XẾP LỰA CHỌN (SELECTION SORT)
Selection sort là một giải thuật sắp xếp tại chỗ (tức không yêu cầu thêm bộ nhớ lưu trữ). Thường được sử dụng để sắp xếp những file nhỏ có giá trị dữ liệu lớn và khóa nhỏ. Bởi vì việc lựa chọn dựa trên khóa và sự hốn vị chỉ được thực hiện khi yêu cầu.
Ưu điểm
Dễ cài đặt.
Không yêu cầu thêm bộ nhớ lưu trữ (In-place sort).
Hạn chế
Độ phức tạp khá lớn khi dữ liệu lớn: O(n2).
Giải thuật
Ta chọn phần tử nhỏ nhất trong N phần tử ban đầu, đưa phần tử này về đầu dãy hiện hành. Sau đó, ta khơng quan tâm đến nó nữa, ta xem dãy hiện hành chỉ còn N-1 phần
29
tử của dãy ban đầu tính từ vị trí thứ 2. Cứ vậy, cho đến khi dãy hiện hành chỉ còn 1 phần tử, ta được 1 dãy sắp tăng.
Các bước tiến hành như sau:
Bước 1: Khởi động i = 0
Bước 2: Tìm phần tử nhỏ nhất a[min] trong dãy hiện hành từ a[i] đến a[N-1] Bước 3: Hoán vị a[min] và a[i]
Bước 4: i = i+1
Nếu i < N-1: quay trở lại bước 2 Ngược lại: STOP!
Ý nghĩa tên giải thuật: giải thuật này được gọi là sắp xếp chọn (selection sort) vì mỗi lần đều lựa chọn giá trị nhỏ nhất để đưa về vị trí thích hợp.
Mơ phỏng giải thuật
Cho dãy số a: 12 2 8 5 1 6 4 15 Sắp xếp dãy số trên tăng dần với các bước của thuật toán được thể hiện như sau: Lần lặp 1: Hoán vị a[min] và a[i]
i = 0, min =4 12 2 8 5 1 6 4 15 Lần lặp 2: Hoán vị a[min] và a[i]
i = 1, min =1 1 2 8 5 12 6 4 15 Lần lặp 3: Hoán vị a[min] và a[i]
i = 2, min =6 1 2 8 5 12 6 4 15 .......
Tương tự sau lần lặp thứ n-1 chúng ta có kết quả:
1 2 4 5 6 8 12 15
30 Độ phức tạp Trường hợp tốt nhất: O (n2) Trường hợp xuất nhất: O (n2) Trường hợp trung bình O (n2) 3.3 | SẮP XẾP CHÈN (INSERTION SORT)
Insertion sort là một sắp xếp so sánh đơn giản và hiệu quả. Trong giải thuật là sự lặp lại thao tác lấy một phần tử ra và chèn vào vị trí đúng của phần danh sách đã được sắp xếp. Việc lặp lại sẽ ngừng khi tất cả các phần tử đã được chèn thành công.
Ưu điểm
Dễ cài đặt
Hiệu quả với dữ liệu nhỏ, đặc biệt khi danh sách đã được sắp xếp một phần thì độ phức tạp là O (n + d) trong đó d là số lượng phần tử bị đảo trật tự.
Hiệu quả hơn selection và bubble sort, thậm chí hơn tất cả các giải thuật có độ phức tạp xất nhất O (n2)
31 Không sử dụng thêm bộ nhớ phụ (in-place)
Mô phỏng giải thuật
Cho dãy số a:
12 2 8 5 1 6 4 15
Sắp xếp dãy số trên tăng dần với các bước của thuật toán được thể hiện như sau: Lần lặp 1: chèn a[i] vào vị trí a[pos+1]
i = 1, pos =-1 12 2 8 5 1 6 4 15 Lần lặp 2: chèn a[i] vào vị trí a[pos+1]
i = 2, pos =0 2 12 8 5 1 6 4 15 Lần lặp 3: chèn a[i] vào vị trí a[pos+1]
i = 1, pos =0 2 8 12 5 1 6 4 15 .......
Tương tự sau lần lặp thứ n-1 chúng ta có kết quả:
1 2 4 5 6 8 12 15
Giải thuật
Lần lượt lấy từng phần tử chèn vào đúng vị trí của phần danh sách đã được sắp xếp (giả sử ban đầu danh sách đã có 1 phần tử được sắp xếp). Công việc này được lặp lại nhiều lần cho tới khi tất cả phần tử của danh sách được chèn xong, lần lặp thứ k là chèn phần tử thứ k vào danh sách đã có k – 1 phần tử đã được sắp xếp.
Các bước thực hiện:
Bước 1: Khởi động với i = 1 //Đoạn a[0] đã được sắp
Bước 2: v = a[i]. Tìm vị trí thích hợp pos trong đoạn a[0]…a[i-1] để chèn v vào
Bước 3: Dời đoạn a[pos]…a[i-1] sang phải để có chỗ đưa v vào.
32 Bước 5: i = i + 1
o Nếu i<n: quay lại bước 2.
o Ngược lại: STOP!
Cài đặt
Đoạn code sau dung để sắp xếp mảng tăng dần
Độ phức tạp
Trường hợp xất nhất xảy ra khi mỗi i phải chuyển tất cả phần tử từ vị trí 0 tới i – 1 (tức khi arrInt[i] nhỏ hơn tất cả phần tử của dãy đã có thứ tự đó). T(n) = O (n2)
Trường hợp tốt nhất: T(n) = O(n) Trường hợp trung bình: T(n) = O (n2)
3.4 | SẮP XẾP ĐỔI CHỖ TRỰC TIẾP (INTERCHANGE SORT) Giải thuật Giải thuật
33
Xuất phát từ đầu dãy, tìm tất cả các cặp nghịch thế và triệt tiêu những cặp nghịch thế này bằng cách hốn đổi vị trí của chúng.
Lưu ý: Cặp (ai, aj) với i < j là cặp nghịch thế nếu giả sử với yêu cầu sắp xếp sao cho ai < aj, nhưng thực tế ai lại lớn hơn aj. Lúc này người ta nói ai và aj là cặp nghịch thế Các bước tiến hành như sau:
Bước 1 : i = 0; // 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 :
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 - 1: ⇒ Bước 2
o Ngược lại ⇒ Kết thúc
Mô phỏng giải thuật
Cho dãy số: 12 2 8 5 1 6 4 15
Sắp xếp dãy số trên tăng dần với các bước của thuật toán được thể hiện như sau:
34 i = 0, j = 1 12 2 8 5 1 6 4 15 i = 0, j = 4 2 12 8 5 1 6 4 15 Lần lặp 2 i = 1, j = 1 1 12 8 5 2 6 4 15 i = 1, j = 3 1 8 12 5 2 6 4 15 i = 1, j = 4 1 5 12 8 2 6 4 15 Lần lặp 3: i = 2, j = 3 1 2 12 8 5 6 4 15 .......
Tương tự sau lần lặp thứ n-1 chúng ta có kết quả:
1 2 4 5 6 8 12 15
35
Độ phức tạp
Trường hợp tốt nhất: T(n) = O(n2) Trườn hợp xất nhất: T(n) = O(n2) Trường hợp trung bình: T(n) = O(n2)
3.5 | SẮP XẾP NỔI BỌT (BUBBLE SORT)
Bubble sort là giải thuật sắp xếp đơn giản nhất. Tư tưởng của giải thuật là so sánh từng cặp và đổi chỗ chúng để thu được trật tự mong muốn, thao tác này được lặp lại cho tới khi khơng cịn cặp nào cần đổi chỗ. Giải thuật này có độ phức tạp cao.
Giải thuật
Xét từ đáy và phần tử nhẹ nổi lên trên. Các bước thực hiện:
Bước 1: Khởi động i = 0
Bước 2: j = N-1 //Duyệt từ cuối dãy về vị trí i
Trong khi j > i thực hiện:
Nếu a[j] < a[j-1]: hoán vị a[j], a[j-1] j = j - 1
36 Bước 3: i = i + 1
Nếu i < N-1: quay trở lại bước 2. Ngược lại: STOP!
Mô phỏng giải thuật
Cho dãy số a : 12 2 4 1 5 8 6 15
Sắp xếp dãy số trên tăng dần với các bước của thuật toán được thể hiện như sau:
Lần lặp 1: i = 0, j=7 12 2 8 5 1 6 4 15 i = 0, j = 4 12 2 8 5 1 4 6 15 i = 0, j = 3 12 2 8 1 5 4 6 15 i = 0, j = 2 12 2 1 8 5 4 6 15 i = 0, j = 1 12 1 2 8 5 4 6 15 1 12 2 8 5 4 6 15 Lần lặp 2: i = 1, j =5 1 12 2 8 5 4 6 15 .......
37
Tương tự sau lần lặp thứ n-1 chúng ta có kết quả:
1 2 4 5 6 8 12 15
Cài đặt
Độ phức tạp
Trường hợp tốt nhất: T(n) = O(n2). Trườn hợp xất nhất: T(n) = O(n2). Trường hợp trung bình: T(n) = O(n2).
38
Phiên bản này cải thiện trường hợp tốt nhất của giải thuật bubble sort từ O(n2) xuống còn O(n). So sánh các giải thuật Bubble sort: mất 𝑛 2 2 lần so sánh và 𝑛 2
2 hốn vị trong cả hai trường hợp trung bình và trường hợp xấu nhất.
Selection sort: mất 𝑛
2
2 lần so sánh và n lần hốn vị. Thích hợp cho trường hợp file nhỏ với dữ liệu lớn hơn và khóa nhỏ.
Insertion sort: mất 𝑛
2
4 lần so sánh và 𝑛
2
8 lần hoán vị trong trường hợp trung bình và gấp đơi trong trường hợp xấu nhất.
Insertion sort gần như là tuyến tính cho trường hợp dữ liệu đã được sắp xếp.
3.6 | SẮP XẾP NHANH (QUICK SORT) Thuật toán: Thuật toán:
39
Kết thúc; //dãy đã được sắp xếp
• Bước 2: Phân hoạch dãy a
left … a right thành các đoạn: a left.. a j,a j+1.. a i-1,a i.. a right Đoạn 1 mid
Đoạn 2: aj+1.. ai-1 = mid Đoạn 3: ai.. aright mid
Với mid là giá trị bất kỳ trong mảng. Thơng thường là giá trị tại vị trí chính giữa của mảng.
• Bước 3: Sắp xếp đoạn 1: a
left.. aj Cách làm như đoạn ban đầu.
• Bước 4: Sắp xếp đoạn 3: a i.. a
right Cách làm như đoạn ban đầu.
Mơ phỏng thuật tốn:
40
Độ phức tạp:
Trường hợp tốt nhất: O(nlog2n). Trường hợp xấu nhất: O(n2). Trường hợp trung bình: O(nlog2n).
3.7 | SẮP XẾP TRỘN (MERGE SORT)
Cho dãy ban đầu a0, a2, …, an-1. Ta ln có thể coi nó là tập hợp liên tiếp của các dãy có thứ tự. Ta gọi các dãy có thứ tự này là các dãy con.
Trong phương pháp Merge Sort, vấn đề là ta tìm cách phân hoạch dãy ban đầu thành các dãy con. Sau khi phân hoạch xong, dãy ban đầu sẽ được tách thành hai dãy phụ theo nguyên tắc phân phối luân phiên dãy con. Sau đó, ta trộn từng cặp dãy con của hai dãy phụ thành một dãy con của dãy ban đầu. Ta nhận thấy số dãy con của dãy ban
41
đầu lúc này giảm đi ít nhất là một nửa. Cứ thế sau một số bước, ta sẽ nhận được dãy ban đầu với số dãy con bằng 1, có nghĩa là ta đã sắp xếp xong.
Có hai phương pháp trộn: Trộn tự nhiên và trộn trực tiếp:
Trộn tự nhiên: Phương pháp này hoạt động trên ý tưởng sử dụng dãy con tự nhiên có
sẵn của dãy ban đầu. Ví dụ với dãy 1 3 5 4 8 9 10 thì chúng ta chỉ có 3 dãy con tăng là 1 3 5 4 8 9 10. Sau đó sẽ tiến hành phân phối đều luân phiên các dãy con này vào 2 dãy phụ, sau khi phân hết dãy ban đầu chúng ta sẽ trộn lại được dãy mới có số lượng dãy con ít hơn. Cứ tiếp tục tách – trộn đến khi nào chỉ thu được một dãy con duy nhất.
Trộn trực tiếp: đây là phương pháp trộn đơn giản nhất. Việc phân hoạch dãy ban đầu
đơn giản như sau: Với dãy ban đầu có n phân tử, ta cứ phân hoạch thành n dãy con. Vì rằng mỗi dãy con chỉ có 1 phần tử nên nó là dãy có thứ tự. Cứ mỗi lần tách – trộn, chiều dài của dãy con sẽ được nhân đơi. Ví dụ với dãy 1 3 5 4 8 9 10 thì ban đầu giả sử có 7 dãy con, mỗi dãy 1 phần tử.
So sánh: Rõ ràng với phương pháp trộn tự nhiên thì số bước lặp tách – trộn sẽ ít hơn vì
nó tận dụng được tình trạng của dãy ban đầu. Nhưng ngược lại để cài đặt bằng phương pháp trộn tự nhiên thì rất phức tạp do chúng ta khó kiểm sốt được số lượng phần tử trong mỗi dãy con tại mỗi bước. Vì vậy trong tài liệu này chúng ta sẽ cùng tìm hiểu thuật tốn trộn trực tiếp.
Thuật giải
Bước 1: Khởi động k = 1 //Với k là chiều dài dãy con
Bước 2: Phân phối dãy a = a0, a2, …, an-1 vào hai dãy phụ b, c theo nguyên
tắc phân phối luân phiên từng dãy con. b = a0, …, ak, a2k+1, …, a3k, …
c = ak+1, …, a2k, a3k+1, …, a4k
Bước 3: Từ 2 dãy phụ b, c, ta trộn từng cặp dãy con và đưa vào dãy a.
Bước 4: k = k * 2
Nếu k < n: quay lại bước 2.
Ngược lại: STOP!
42
Sắp xếp dãy số tăng dần:
38 27 43 3 9 82 10
43
Độ phức tạp
44
Trường hợp xấu nhất: O(nlogn). Trường hợp trung bình: O(nlogn).
3.8 | SẮP XẾP VUN ĐỐNG (HEAP SORT) Định nghĩa Heap: Định nghĩa Heap:
Mỗi mảng a[1..n] có thể xem như một cây nhị phân gần đầy (có trọng số là các giá trị của mảng), với gốc ở phần tử thứ nhất, con bên trái của đỉnh a[i] là a[2*i] con bên phải là a[2*i+1] (nếu mảng bắt đầu từ 1 còn nếu mảng bắt đầu từ 0 thì hai con là a[2*i+1] và a[2*i+2]) (nếu 2*i<=n hoặc 2*i+1<=n, khi đó các phần tử có chỉ số lớn hơn n/2 khơng có con, do đó là lá).
Ví dụ mảng (45,23,35,13,15,12,15,7,9) là một đống
Một cây nhị phân, được gọi là đống cực đại nếu khóa của mọi nút khơng nhỏ hơn khóa các con của nó. Khi biểu diễn một mảng a[] bởi một cây nhi phân theo thứ tự tự nhiên điều đó nghĩa là a[i]>=a[2*i] và a[i]>=a[2*i+1] với mọi i=1..int(n/2). Ta cũng sẽ gọi mảng như vậy là
đống. Như vậy trong đống a[1] (ứng với gốc của cây) là phần tử lớn
nhất. Mảng bất kỳ chỉ có một phần tử ln ln là một đống.
Một đống cực tiểu được định nghĩa theo các bất đẳng thức ngược lại: a[i]<=a[2*i] và a[i]<=a[2*i+1]. Phần tử đứng ở gốc cây cực tiểu là phần tử nhỏ nhất.
45
Việc sắp xếp lại các phần tử của một mảng ban đầu sao cho nó trở thành đống được gọi là vun đống.
Giải thuật
Bước 1: Xây dựng heap từ mảng ban đầu.
Bước 2: Hoán vị phần tử đầu và cuối mảng, rồi giảm dần số phần tử của mảng
xuống thành n-1 (bỏ phần tử cuối).
Bước 3: Tiếp tục xây dựng và hoán vị như trên cho đến hết ta sẽ có dãy được
sắp thứ tự tăng dần (muốn sắp giảm dần ta chi cần cho phần tử gốc của heap là phần tử nhỏ nhất)
Mơ phỏng giải thuật
Ví dụ: Cho dãy : 15 , 19 , 10 , 7 , 17 , 16
Giai đoạn 1: tạo heap: i= [0+length]/2=[0+5]/2=2, (ai, a2*i); (ai , a2*i+1) trong đó: (ai,a2*i), (ai, a2*i+1) là các cặp phần tử liên đới, nếu thỏa thì i = i -1, khơng thi hốn vị (ai, a2*i+1).
46
47
Giai đoạn 2: Sắp xếp dựa trên heap, phần tử đầu heap lớn nhất dãy, trong quá trình
sắp xếp nếu mảng khơng cịn là heap thì phải hiệu chỉnh lại.
48
Sau khi đưa phần tử ở gốc cây (phần tử lớn nhất của cây Heap) ra khỏi cây (đưa về cuối mảng) thì chúng ta tiếp tục bước chỉnh cây cịn lại thành cây Heap. Nhưng không cần phải lặp lại thao tác từ đầu mà chỉ lan chuyền hiệu chỉnh trên những nút bị tác động khi bỏ phần tử ở gốc cây ra ngoài.
49
Đến đây ta lại thu được cây có tính chất Heap mới. Và tiếp tục lấy nút ở gốc cây ra khỏi cây (đưa về cuối dãy hiện hành). Và cứ như thế cho đến khi cây chỉ cịn 1 phần tử thì ngừng lại.
53
Cài đặt
54
Giai đoạn hiệu chỉnh a0,..an-1 thành Heap
55
Độ phức tạp
Trường hợp tốt nhất T(n) = O(nlogn). Trường hợp xấu nhất T(n) = O(nlogn). Trường hợp trung bình T(n) = O(nlogn).
BÀI TẬP
Xây dựng ứng dụng quản lý sinh viên của trường ABC. Biết rằng mỗi sinh viên cần lưu trữ thông tin sau:
- Mã sinh viên: kiểu chuỗi
- Họ tên: kiểu chuỗi (có khoảng trắng) - Năm sinh: số nguyên
- Danh sách các môn học (Mỗi môn học cần lưu trữ Mã môn, tên mơn, số tín chỉ, điểm. Và số mơn học có thể khác nhau đối với các Sinh viên khác nhau) struct MonHoc { string mamh; string tenMon; int tinChi;
56 float diem }; struct SinhVien { string masv; string hoTen; int namSinh; MonHoc arrMon[30]; int nSoMon; }; Các chức năng cần có trong ứng dụng: 1. Nhập một danh sách gồm n Sinh viên
2. Hiển thị danh sách sinh viên lên màn hình (định dạng xuất dưới dạng bảng).
MaSV: tt1 Họ tên: Le Hoa
Năm Sinh: 1990
Danh Sách 2 môn học đã học:
Mã Môn Tên Mơn Tín chỉ ĐTB
Ctdl Cấu trúc dữ liệu > 3 5 Ktlt1 Kỹ thuật lt1 3 5
************************************************
MaSV: tt2 Họ tên: Le Tuan
Năm Sinh: 1990
Danh sách 2 môn học đã học:
Mã Môn Tên Mơn Tín chỉ ĐTB
th Tin học 3 7
57
3. Tìm kiếm sinh viên có mã sinh viên là x, sau đó sửa họ tên của sinh viên thành y (sử dụng thuật tốn tìm kiếm tuần tự) (x, y được cấp từ người dùng) 4. Nhập vào 2 số x, y (x < y) và đếm xem có bao nhiêu Sinh viên có điểm
trung bình nằm trong đoạn [x, y].
Biết cơng thức tính điểm trung bình = (diemMon1 * tinChi1 + diemMon2 * tinChi2 +…)/ (tinChi1 + tinChi2+…)
5. Sắp xếp các sinh viên theo họ tên giảm dần và in kết quả ra màn hình( dùng