You are on page 1of 63

BÀI 3

Sắp xếp

1
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 theo một thứ tự
nhất định.

 Ví dụ:
– {1, 2, 5, 7, 9, 12}, {14, 12, 7, 5, 2, 1}
– {“An” “Binh” “Dương” “Hương”}
 Việc sắp xếp là một bài toán phổ biến 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 xuất cho các bảng
biểu...

2
Sắp xếp

 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

 Trường tham gia quá trình tìm kiếm gọi là khoá (key)

 Việc sắp xếp sẽ được tiến hành dựa vào giá trị khoá này

VD: Sinh viên (MaSV, Hoten, Ngaysinh)

3
Sắp xếp

 Các phương pháp sắp xếp


1. Interchange Sort
2. Bubble Sort
3. Selection Sort
4. Insertion Sort
5. Shell sort
6. Quick sort
7. Radix sort
8. Merge sort
9. Heap sort

4
Sắp xếp

 Mô tả bài toán:
– Cho tập N phần tử có m thuộc tính, được biểu diễn dưới
dạng bản ghi. Dựa vào một (hoặc vài) thuộc tính để sắp xếp
các phần tử theo trật tự mới.
– Dựa theo khóa sắp xếp định vị lại thứ tự bản ghi
– Chuyển các bản ghi về vị trí mới
 Hai thao tác cơ bản:
– So sánh
– Gán (để thay đổi giá trị)

5
1. Interchange Sort
 Ý tưởng: đổi chỗ trực tiếp

– Xuất phát từ đầu dãy, lần lượt tìm những phần tử


còn lại sai thứ tự so với phần tử đang xét. Với mỗi
phần tử tìm được mà sai thứ tự, thực hiện đổi chỗ
để thoả thứ tự.
– Lặp lại tương tự với các phần tử tiếp theo

6
 Đổi chỗ: hoán vị giá trị của 2 ô nhớ
Trước:

4 1 void Swap (int &x, int &y)


x y {

Sau: }

x y

7
Interchange Sort

54 28 36 66
0 1 2 3

i j

i=0, j=1, 2, 3: so sánh ? Cho i chạy từ đâu tới đâu?


i=1, j=3, 4: Cho j chạy từ đâu tới đâu?
i=2, j=3: Nếu a[i] ? a[j] thì
Đổi chỗ a[i] và a[j]

8
1. Interchange Sort

Các bước tiến hành


B1: i = 0; // bắt đầu từ đầu dãy
B2: j = i +1; // duyệt qua các phần tử sau
B3: Chừng nào j < n thực hiện:
Nếu a[j] < a[i] thì Đổi chỗ a[i] và a[j];
j = j +1;
B4: i = i +1;
Nếu i<n-1 thì lặp lại B2;
Ngoài ra  Kết thúc!

9
1. Interchange Sort

void InterchangeSort(int a[], int n)


{
int i, j;
for(i =0; i < n-1; i++)
for(j=i+1; j <n; j++)
if (a[i] >a[j]) //đổi chỗ cặp phân tử đứng sai
Swap(a[i], a[j]);
}

10
1. Interchange Sort

10 3 7 6 2 5 4 16

11
1. Interchange Sort
 Bài tập áp dụng
– Viết hàm sắp xếp danh sách SV tăng dần theo điểm
trung bình

12
2. Bubble Sort
 Ý tưởng chính: sắp xếp nổi bọt
– Xuất phát từ cuối dãy, đổi chỗ các cặp phần tử kế cận để đưa phần
tử nhỏ hơn về đầu.
– Sau đó ở bước tiếp theo không xét phần tử đó nữa. Do vậy lần xử
lý thứ i sẽ có vị trí đầu dãy là i.
– Lặp lại xử lý trên cho đến khi không còn cặp phần tử nào được xét.
Þ Đưa từ từ phần tử nhỏ nhất về đầu dãy

Hoặc: đưa từ từ phần tử lớn nhất về cuối dãy?

13
2.2 Bubble Sort
 Các bước tiến hành
– B1: i=0; // lần xử lý đầu tiên
– B2: j=n-1; // duyệt từ cuối dãy ngược về vị trí i
Trong khi (j>i) thực hiện:
Nếu a[j] < a[j-1]: Hoán đổi a[j] và a[j-1]
j = j -1;
– B3: i = i+1; // lần xử lý kế tiếp
Nếu i > n-2: Hết dãy  Dừng
Ngược lại: quay lại B2

14
Minh họa Bubble Sort

<
12 2 8 5 1 6 4 15

i=0 j=6 j=7

12 2 8 5 1 4 6 15

i=0 j=4

12 2 8 1 5 4 6 15

i=0 j=3

15
Minh họa Bubble Sort

12 2 1 8 5 4 6 15

i=0 j=2

12 1 2 8 5 4 6 15

i=0 j=1

1 12 2 8 5 4 6 15

i=1 j=5
16
Minh họa Bubble Sort

1 12 2 8 4 5 6 15

i=1 j=4

1 12 2 4 8 5 6 15

i=1 j=2

1 2 12 4 8 5 6 15

i=2 j=5
17
Minh họa Bubble Sort

1 2 12 4 5 8 6 15

i=2 j=3

1 2 4 12 5 8 6 15

i=3 j=6

1 2 4 12 5 6 8 15

i=3 j=4
18
Minh họa Bubble Sort

1 2 4 5 12 6 8 15

i=4 j=5

1 2 4 5 6 12 8 15

i=5 j=6

1 2 4 5 6 8 12 15

i=6

1 2 4 5 6 8 12 15
19
Cài đặt Bubble Sort

54 28 36 66
0 1 2 3

i j
i=0, j=3, 2, 1: so sánh ? Cho i chạy từ đâu tới đâu?
i=1, j=3, 2: Cho j chạy từ đâu tới đâu?
i=2, j=3: Nếu a[…] ? a[…] thì
Đổi chỗ a[…] và a[…]

20
Cài đặt Bubble Sort
void BubbleSort(int a[], int n)
{
int i, j;
for(i =0; i < n-1; i++)
for(j=n-1; j >i; j--) //đổi chỗ cặp phân tử đứng sai
if (a[j-1]>a[j])
Swap(a[j], a[j-1]);
}

21
Bài tập

 Hãy cho kết quả chạy từng bước khi sắp xếp

tăng dãy số sau: 6 5 4 3 2 1


 Bằng phương pháp:

– Interchange
– Bubble

– Selection

22
Bài tập

1. Sắp xếp danh sách tăng theo DTB= IS / BS/ SS


2. Sắp xếp danh sách giảm theo masv = IS / BS/
SS

23
3. Selection Sort

Ý tưởng chính
 Chọn phần tử có khóa nhỏ nhất trong N phần tử ban đầu.
 Đổi chỗ phần tử vừa chọn với phần tử đầu dãy
 Xem dãy hiện hành chỉ còn N-1 phần tử (không xét phần
tử đầu)
– Bắt đầu từ vị trí thứ hai
– Lặp quá trình trên cho dãy hiện hành… đến khi dãy hiện hành
chỉ còn một phần tử

24
3. Selection Sort

 Các bước thực hiện:


– B1: i = 0
– B2: Tìm phần tử nhỏ nhất trong dãy hiện
hành từ a[i] đến a[n-1]: a[?]
– B3: Hoán vị a[i] và a[vt_min]
– B4: Nếu i < n -2 thì i = i+1  Lặp B2
Ngược lại  Dừng

25
Minh họa Selection

12 2 8 5 1 6 4 15
i=0 Vt_min=4

1 2 8 5 12 6 4 15
i=1

1 2 8 5 12 6 4 15
i=2 Vt_min=6
26
Minh họa Selection Sort
1 2 4 5 12 6 8 15
i=3

1 2 4 5 12 6 8 15
i=4 Vt_min=5

1 2 4 5 6 8 12 15
i=6

1 2 4 5 6 8 12 15
27
Cài đặt Selection Sort

54 28 36 66
0 1 2 3

i vt_min=?
Lặp với mỗi vị trí i=0, 1, 2, ….?
vt_min=?
vt_min= ?
j= 1, 2, 3:
Cho j chạy từ đâu tới đâu?
nếu a[j] <?
Nếu a[…] ? a[…] thì
vt_min=?
cập nhật vt_min
Đổi chỗ a[…] với a[…]
28
Cài đặt Selection Sort
void SelectionSort(int a[], int n)
{
for(int i = 0; i < n-1; i++)
{ // duyệt qua n-1 phần tử
int vtmin = i;
for(int j = i+1; j < n; j++)
if (a[j] < a[vtmin])
vtmin = j;
if (i!=vtmin)
Swap(a[vtmin], a[i]); //hoán vị a[i] và phần tử nhỏ
nhất
}
} 29
Bài tập

 Viết hàm sắp xếp danh sách sinh viên tăng dần

theo điểm trung bình / (họ tên) bằng phương


pháp:
– Interchange
– Bubble
– Selection

30
4. Insertion Sort
 Ý tưởng chính
– Cho dãy ban đầu a[0], a[1],.., a[n-1], ta có thể xem dãy con
gồm một phần tử a[0] đã được sắp.
– Sau đó thêm a[1] vào đoạn a[0] sao cho a[0] a[1] được sắp.
– Tiếp tục thêm a[2] vào để có a[0] a[1] a[2] được sắp....
– Cho đến khi thêm xong a[n-1]
vào đoạn a[0] a[1]...a[n-2]
 đoạn a[0] a[1]...a[n-2] a[n-1]
được sắp

31
2.4 Insertion Sort
 Các bước tiến hành
– B1: i = 1; //giả sử có đoạn a[0] đã được sắp
– B2: x= a[i];
Tìm được vị trí cần chèn x vào là pos
– B3: Dời chỗ các phần tử từ a[pos]  a[i-1] sang phải một
vị trí để dành chỗ cho a[i].
– B4: a[pos] = x; // có đoạn a[0]...a[i] được sắp.
– B5: i = i +1;
Nếu i < n: Lặp lại B2
Ngược lại: Dừng  Dãy đã được sắp
 Ví dụ: minh họa phương pháp chèn với dãy:
12 2 8 5 1 6 4 15

32
Minh họa Insertion Sort

12 2 8 5 1 6 4 15

i=1

2 12 8 5 1 6 4 15

i=2

2 8 12 5 1 6 4 15

i=3

33
Minh họa Insertion Sort

2 5 8 12 1 6 4 15

i=4
Tương
tự

1 2 4 5 6 8 12 15

34
Cài đặt Insertion Sort
void InsertionSort(int a[], int n){
int pos , x; //x lưu phần tử a[i]
for(int i=1; i < n; i++){
x = a[i]; pos = i-1; //xét từ vị trí i trở về trước
while (pos ≥ 0 && a[pos] > x) {
//kết hợp dời chỗ các p. tử đứng sau x trong dãy mới
a[pos+1] = a[pos];
pos--;
}
a[pos+1] = x; //chèn x vào dãy mới
}
}

35
Cài đặt Insertion Sort
void InsertionSort(SV a[], int n){
int pos; SV x; //x lưu phần tử a[i]
for(int i=1; i < n; i++){
x = a[i]; pos = i-1; //xét từ vị trí i trở về trước
while (pos ≥ 0 && a[pos].DTB > x.DTB) {
//kết hợp dời chỗ các p. tử đứng sau x trong dãy mới
a[pos+1] = a[pos];
pos--;
}
a[pos+1] = x; //chèn x vào dãy mới
}
}

36
 Cho kết quả chạy từng bước khi thực hiện sắp
xếp tăng dãy số sau:
5 7 4 6 1 3 2
Bằng phương pháp sắp xếp chèn

37
Bài tập

 Cài đặt 4 thuật toán đã cho với yêu cầu sắp

mảng giảm dần


 Cài đặt trên mảng các cuốn sách

– Sắp xếp theo mã sách


– Sắp xếp theo tên sách

38
5. Shell Sort
 Ý tưởng chính

– Thuật toán Insertion sort có hạn chế là luôn chèn 1


phần tử vào đầu dãy!
– Shell Sort cải tiến bằng cách chia làm nhiều dãy con
và thực hiện pp chèn trên từng dãy con

39
5. Shell Sort
 Xét một dãy a[0]...a[n-1], cho một số nguyên h
(1  h  n), chia dãy thành h dãy con như sau:
– Dãy con 1: a[0], a[0+h], a[0+2h]...
– Dãy con 2: a[1], a[1+h], a[1+2h]...
– Dãy con 3: a[2], a[2+h], a[2+2h]...
– ...
– Dãy con h: a[h], a[2h], a[3h]...

40
2.5 Shell Sort

VD: cho dãy n = 8, h = 3


10 3 7 6 2 5 4 16
Dãy chính 10 3 7 6 2 5 4 16

Dãy con 1 10 6 4

Dãy con 2 3 2 16

Dãy con 3 7 5

41
5. Shell Sort

 Với mỗi bước h, áp dụng Insertion Sort trê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 h =

1.
 Khi h =1 thực hiện Insertion Sort trên 1 dãy duy nhất là

dãy chính
 Kết quả được dãy phần tử được sắp.

42
5.Shell Sort
 Các bước tiến hành chính
– B1: chọn k khoảng cách h[1], h[2],..,h[k], và i = 1;
– B2: 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ắp xếp từng dãy con bằng
Insertion sort.
– B3: i = i+1
Nếu i > k:  Dừng
Ngược lại:  Bước 2.

43
5. Shell Sort
 Cho dãy bên dưới với n = 8, h = {5, 3, 1}.
h1 = 5 10 3 7 6 2 5 4 16

Dãy 1 10 5

Dãy 2 3 4
Dãy 3 7 16

Dãy 4 6
Dãy 5 2
44
5. Shell Sort
5 3 7 6 2 10 4 16

h2 = 3

Dãy 1
5 6 4

Dãy 2
3 2 16

Dãy 3
7 10

4 2 7 5 3 10 6 16
45
5. Shell Sort

h3 = 1
Dãy 1

4 2 7 5 3 10 6 16

Sắp xếp chèn

2 3 4 5 6 7 10 16

46
5. Shell Sort
// h[] chứa các bước nhảy, k là số bước nhảy
void ShellSort(int a[], int n, int h[], int k){
int step, i, pos, x, len;
for(step = 0; step < k; step++) { // duyệt qua từng bước nhảy
len = h[step]; // chiều dài của bước nhảy
for(i = len; i < n; i++) { // duyệt các dãy con
x = a[i]; // lưu phần tử cuối để tìm vị trí thích hợp trong dãy con
pos = i – len; // a[j] đứng trước a[i] trong cùng dãy con
while ((x < a[pos]) && (pos ≥ 0)) {
a[pos+len] = a[pos]; // dời về sau theo dãy con
pos = pos – len; // qua phần tử trước trong dãy con
}
a[pos+len] = x; // đưa x vào vị trí thích hợp trong dãy con
}// end for i
}// end for step
}

47
5. Shell Sort

5 3 7 6 2 10 4 16
Len=h2 = 3
j=0 j=3
i=3 i=6

5 6 4
j=1 i=4 i=7

3 2 16
j=2 i=5

7 10

4 2 7 5 3 10 6 16
48
6. Quick Sort
 Thuật toán do Hoare đề xuất
– Tốc độ trung bình nhanh hơn thuật toán khác
– Do đó Hoare dùng “quick” để đặt tên
 Ý tưởng chính
– QS phân hoạch dãy ban đầu thành hai phần dựa
vào một giá trị x
• Dãy 1: gồm các phần tử a[i] ko lớn hơn x (<=x)
• Dãy 2: gồm các phần tử a[i] ko nhỏ hơn x (>=x)

49
6. Quick Sort
 Giải thuật sắp xếp dãy a[left], a[left+1],...,a[right]
được phát biểu đệ quy như sau:
 B1: Phân hoạch dãy a[left]...a[right] thành các dãy
con:
– Dãy con 1: a[left]...a[j] < x
– Dãy con 2: a[j+1]...a[i-1] = x
– Dãy con 3: a[i]...a[right] > x
 B2:
– Nếu (left < j) // dãy con 1 có nhiều hơn 1 phần tử
Phân hoạch dãy a[left]...a[j]
– Nếu (i < right) // dãy con 3 có nhiều hơn 1 phần tử
Phân hoạch dãy a[i]...a[right]

50
6. Quick Sort
 Sau khi phân hoạch thì dãy ban đầu được phân thành ba phần:
– a[k] < x, với k = 1...i
– a[k] = x, với k = i..j
– a[k] > x, với k = j..n

a[k] < x a[k] = x a[k] > x

 Nếu đoạn 1, đoạn 3 đều có 1 phần tử thì dãy ban đầu được sắp
 Nếu đoạn 1 hoặc đoạn 3 có nhiều hơn 1 phần tử thì ta cần sắp lại
đoạn đó (áp dụng phân hoạch như trên)

51
6. Quick Sort
 Giải thuật phân hoạch dãy a[left], a[left+1],...,a[right] thành hai
dãy con:
 B1: Chọn tùy ý một phần tử a[k] trong dãy là giá trị mốc, left  k
 right,
– Cho x = a[k], i = left, j = right.
 B2: Tìm và hoán vị cặp phần tử a[i] và a[j] đứng sai thứ tự.
– B2-1: Trong khi a[i] < x  i++; //Tìm a[i] ≥ x
– B2-2: Trong khi a[j] > x  j--; //Tìm a[j] ≤ x
– B2-3: Nếu i < j  Swap(a[i], a[j]) // a[i], a[j] sai thứ tự
 B3:
– Nếu i < j:  Bước 2;
– Nếu i  j:  Dừng.

52
6. Quick Sort
void QuickSort(int a[], int left, int right) {
int i, j, x;
x = a[(left+right)/2]; // chọn phần tử giữa làm mốc
i = left, j = right;
do {
while (a[i] < x) i++; // lặp đến khi a[i] >= x
while (a[j] > x) j--; // lặp đến khi a[j] <= x
if ( i <= j) {
Swap(a[i], a[j]);
i++; // qua phần tử kế tiếp
j--; // qua phần tử đứng trước
}
} while (i<j);
if (left < j) // ph đoạn bên trái
QuickSort(a, left, j);
if (i< right) // ph đoạn bên phải
QuickSort(a, i, right);
}

53
Minh họa Quick Sort

Phân hoạch dãy

i j
0 1 2 3 4 5 6 7

12 2 8 X 5 1 4 6 15

Left Right

Stop Stop

Not < X Not > X

54
Minh họa Quick Sort

j i
0 1 2 3 4 5 6 7

4 2 1 5 8 12 6 15

Left Right

J>left => sắp xếp bằng i<right => sắp xếp bằng
cách phân hoạch tiếp cách phân hoạch tiếp

55
7. Radix Sort
 Không quan tâm đến việc so sánh giá trị các
phần tử
 Bản thân việc phân loại và trình tự phân loại
sẽ tạo ra thứ tự cho các phần tử
 Còn gọi là phương pháp phân lô

56
7. RadixSort
 493 812 715 710 195 437 582 340 385
Số hàng đv Dãy con
Phân lô hàng đv 0 710 340
1
2 812 582
3 493
4
5 715 195 385
6
Sau khi phân lô
7 437
theo hàng đơn vị
8
9

57
710 340 812 582 493 715 195 385 437
Số hàng chục Dãy con
Phân lô hàng chục 0
1 710 812 715
2
3 437
4 340
5
Sau khi phân lô 6
theo hàng chục 7
8 582 385
9 493 195

710 812 715 437 340 582 385 493 195


58
7. RadixSort
710 812 715 437 340 582 385 493 195
Số hàng trăm Dãy con
Phân lô hàng trăm 0
1 195
2
3 340 385
4 437 493
5 582
6
Sau khi phân lô 7 710 715
theo hàng trăm 8 812
9

195 340 385 437 493 582 710 715 812

59
7. RadixSort
 GT RadixSort thực hiện như sau:
 Xem mỗi phần tử a[i] trong dãy a[1]...a[n] là
một số nguyên có tối đa m chữ số
 Lần lượt phân loại các chữ số theo hàng đơn
vị, hàng chục, hàng trăm...
– Tại mỗi bước phân loại ta sẽ nối các dãy con từ
danh sách đã phân loại theo thứ tự 0  9.
– Sau khi phân loại xong ở hàng thứ m cao nhất, ta
sẽ thu được danh sách các phần tử được sắp.

60
2. Sắp xếp
2.7 RadixSort
 B1: k = 0; // k thể hiện chữ số phân loại, k=0 hàng đơn vị, k=1
hàng chục...
 B2: //Tạo các dãy chứa các phần tử phân loại B[0]...B[9]
– Khởi tạo B[0]...B[9] rỗng,
– B[i] sẽ chứa các phần tử có chữ số thứ k là i.
 B3:
– For i=1 to n do
Đặt a[i] vào dãy B[j] với j là chữ số thứ k của a[i].
– Nối B[0], B[1],..., B[9] lại theo đúng trình tự thành dãy a.
 B4:
– k = k +1
– Nếu k < m:  Bước 2. //m là số lượng chữ số tối đa của các số
– Ngược lại:  Dừng.

61
7. RadixSort
void RadixSort(long a[], int n){
int i, j, d, digit, num;
int h = 10; // biến để lấy các con số, bắt đầu từ hàng đơn vị
long B[10][MAX]; //mảng hai chiều chứa các phần tử phân lô
int Len[10]; // kích thước của từng mảng B[i]
for(d = 0; d < MAXDIGIT; d++) {// xét lần lượt hàng
for( i = 0; i < 10; i++) // khởi tạo kích thước các dãy B[i] là 0
Len[i] = 0;
for(i = 0; i < n; i++) { // duyệt qua tất cả các phần tử của mảng
digit = (a[i] % h) / (h / 10);// lấy chữ số theo hàng h
B[digit][Len[digit]++] = a[i];
}
num = 0; // chỉ số bắt đầu cho mảng a[]
for(i = 0; i < 10; i++) // duyệt qua các dãy từ B[0] – đến B[9]
for(j =0; j < Len[i]; j++)
a[num++] = B[i][j];
h *= 10; // qua hàng kế tiếp.
}
} //end RadixSort
62
63

You might also like