Thuật toán sử dụng tưtưởng chia để trị nên còn được ví là part sort và thuộc loại sắp xếp phức tạp.. Từ dãyban đầu, người ta sẽ chia dãy thành hai phần nhỏ theo một quy tắc xác định, từ
Trang 1TRƯỜNG ĐẠI HỌC KIẾN TRÚC HÀ NỘI
KHOA CÔNG NGHỆ THÔNG TIN
BÀI TẬP LỚN Môn: Cấu trúc dữ liệu và giải thuật
ĐỀ TÀI:
Sắp xếp nhanh TÌM HIỂU THUẬT GIẢI TÌM KIẾM
HEURISTIC NHÓM THỰC HIỆN:
1 Trần Đăng Quyết (nhóm trưởng)
2 Nguyễn Thanh Tùng
3 Nguyễn Tuấn Anh
4 Nguyễn Hữu Hiếu Giảng viên hướng dẫn : Nguyễn Đăng Khoa.
Hà Nội, 2021.
Trang 2Mục lục :
1 Giới thiệu 3
2 Ý tưởng thuật toán 3
3 Các bước thực hiện giải thuật 6
4 Cài đ t gi i thu t ặ ả ậ 8
5 Độ phức tạp của thuật toán 9
6 Đánh giá thu t toán ậ 9
6.1 u đi m Ư ể 9
6.2 Nh ượ c đi m ể 10
7 Bài t p quick-sort ậ 10
Trang 3Sắp xếp nhanh (Quick Sort)
1 Giới thiệu
Thuật toán sắp xếp nhanh hay còn gọi là QuickSort Algorithm là một trong 6 thuật toán sắp xếp thông dụng nhất của khoa học máy tính Thuật toán sử dụng tư tưởng chia để trị nên còn được ví là part sort và thuộc loại sắp xếp phức tạp Từ dãy ban đầu, người ta sẽ chia dãy thành hai phần nhỏ theo một quy tắc xác định, từ đó giúp cải thiện tốc độ của việc săp xếp
Hình minh họa Quick sort
Thuật toán sắp xếp nhanh được phát triển vào năm 1959 bởi Tony Hoare khi ông đang là sinh viên thỉnh giảng tại Đại học Tổng hợp Moscow Khi đó, Hoare đang thực hiện một dự án về máy dịch cho Phòng thí nghiệm Vật lý Quốc gia Là một phần của quá trình dịch thuật, ông phải sắp xếp các từ của các câu tiếng Nga trước khi tra cứu chúng trong từ điển Nga-Anh đã được sắp xếp theo thứ tự bảng chữ cái
và được lưu trữ trong băng từ Để hoàn thành nhiệm vụ này, ông đã phát hiện ra Quick Sort và sau đó đã xuất bản mã năm 1961
Quick Sort là thuật toán đáng được quan tâm và thực sự rất quan trọng Hầu hết trong các thư viện của các ngôn ngữ lập trình hiện nay đều có sẵn thuật toán này
2 Ý tưởng thuật toán
Quicksort là thuật toán ứng dụng ý tưởng chia nhỏ để trị Đầu tiên nó chia mảng
đầu vào thành hai mảng con một nửa nhỏ hơn, một nửa lớn hơn dựa vào một phần
tử trung gian Sau đó, nó sắp xếp đệ quy các mảng con để giải quyết bài toán
Trang 4Mấu chốt của thuật toán ở việc chia mảng con hay còn gọi là thuật toán phân đoạn Cách giải quyết có thể tóm tắt như sau:
Chọn một phần tử trong mảng làm phần tử trung gian để chia đôi mảng gọi là pivot Thông thường ta thường chọn phần tử đầu tiên, phần tử ở giữa mảng hoặc phần tử cuối cùng của mảng để làm pivot
Phân đoạn: di chuyển phần tử có giá trị nhỏ hơn pivot về một bên, tất cả các phần tử có giá trị lớn hơn pivot xếp về một bên (các giá trị bằng pivot
có thể đi theo một trong hai cách)
Sau bước phân đoạn di chuyển pivot về đúng vị trí giữa của 2 mảng con
Áp dụng đệ quy các bước phân đoạn trên cho hai mảng con để thực hiện sắp xếp
Trường hợp dừng của đệ quy là khi các mảng con có kích thước bằng 0 hoặc 1, khi
đó nó đã được sắp xếp theo định nghĩa, vì vậy chúng không bao giờ cần phải sắp xếp
Trang 5Lưu đồ thuật toán sắp xếp nhanh:
3 Các bước thực hiện giải thuật
Giả sử ta có một dãy các phần tử như sau:
Left = 0; right = 7; Ở ví dụ này ta lấy một phần tử làm khóa, ở đây mình chọn phần
tử khóa là A[(0+7)/2] = A[3] = 30 Cho i chạy từ trái sang phải, bắt đầu từ vị trí 0 Cho j chạy từ phải sang trái, bắt đầu từ vị trí 7 Quá trình duyệt từ bên trái với biến duyệt i sẽ dừng lại ở 56 (i=2), vì đây không phải là phần tử nhỏ hơn khóa Quá trình duyệt từ bên phải với biến duyệt j sẽ dừng lại ở 18 (j=7) vì đây là phần tử nhỏ hơn khóa Tiến hành đổi chỗ 2 phần tử cho nhau
Quá trình duyệt tiếp tục Khóa ở đây vẫn là 30 Ta tiếp tục tăng biến duyệt i và giảm biến duyệt j, nhận thấy biến duyệt i chạy đến 30 (i=3), còn biến duyệt j dừng lại ở
24 (j=6) Tiến hành đổi chỗ 2 phần tử cho nhau
Trang 6Quá trình duyệt tiếp tục Khóa ở đây vẫn là 30 Tiếp tục tăng biến duyệt i và giảm biến duyệt j, nhận thấy biến duyệt i chạy đến 32 (i=5), còn biến duyệt j dừng lại ở 30(j=6) Tiến hành đổi chỗ 2 phần tử cho nhau
Quá trình duyệt tiếp tục Khóa ở đây vẫn là 30 Tiếp tục tăng biến duyệt i và giảm biến duyệt j, nhận thấy biến duyệt i và j cùng chạy đến 30 Lúc này ta sẽ chia mảng trên ra làm 2 mảng con A[0 4] và A[5 7], vì ta thấy A[5 7] đã được sắp xếp tăng dần rồi nên ta sẽ xét A[0 4] A[0 4] = [28, 16, 18, 24, 17] Lặp lại những bước trên với phần tử được chọn là khóa là A[(0+4)/2] = A[2] = 18 left = 0; right = 4; Cho i chạy từ trái sang phải bắt đầu từ vị trí 0 Cho j chạy từ phải sang trái bắt đầu từ vị trí
4 Quá trình duyệt từ bên trái với biến duyệt i sẽ dừng lại ở 28(i=0) vì đây không phải là phần tử nhỏ hơn khóa Quá trình duyệt từ bên phải với biến duyệt j sẽ dừng lại ở 17 (j=4) vì đây là phần tử không lớn hơn khóa Tiến hành đổi chỗ 2 phần tử cho nhau
Trang 7Quá trình duyệt tiếp tục Tăng biến duyệt i và giảm biến duyệt j Lúc này i = j nên ta
sẽ chia mảng trên ra làm 2 mảng con A[0 1] và A[2 4] Ta thấy A[2 4] đã được sắp xếp tăng dần rồi, nên ta chỉ xét A[0 1], vì A[0] > A[1] nên ta đổi chỗ 2 phần tử cho nhau và được mảng sắp xếp tăng dần cuối cùng:
4 Cài đặt giải thuật
Như đã nói ở trên, Quick sort cũng là một thuật toán đệ quy, bao gồm việc chia mảng thành 2 mảng con thỏa mãn yêu cầu trên, sau đó thực hiện lời gọi đệ quy với 2 mảng con vừa tạo được Giả sử mảng được giới hạn bởi 2 tham số left và right cho biết chỉ số đầu và cuối của mảng, khi đó thuật toán được cài đặt như sau:
void QuickSort(int left, int right)
{
int x = A[(left + right)/2]; //luu gia tri phan tu giua mang
int i = left;
int j = right;
do {
//tim phan tu khong nho hon x theo i
while(A[i] < x)
i++;
//tim phan tu khong lon hon x theo j
while(A[j] > x)
j ;
//neu i nam truoc j thi thuc hien hoan vi
Trang 8if(i <= j) {
swap(A[i], A[j]); //hoan vi 2 phan tu tai
vi tri i va j
//sau khi hoan vi thi tang i va giam j i++;
j ;
}
} while(i < j);
//neu left < j thi tiep tuc chia mang de sap xep
if (left < j)
QuickSort(left, j);
//neu i < right thi tiep tuc chia mang de sap xep
if (i < right)
QuickSort(i, right);
}
5 Độ phức tạp của thuật toán
Do hiệu quả của thuật toán phụ thuộc rất nhiều vào việc phần tử nào được chọn là phần tử chốt nên độ phức tạp thuật toán cũng phụ thuộc vào đó mà khác nhau
Phân đoạn không cân bằng: không có phần nào cả, một bài toán con có kích thước n-1 và bài toán kia có kích thước là 0 Đó là trường hợp xấu nhất xảy
ra khi dãy đã cho là dãy đã được sắp xếp và phần tử chốt được chọn là phần
tử đầu của dãy => độ phức tạp thuật toán sẽ là O(n^2)
Phân đoạn hoàn hảo: phân đoạn luôn thực hiện dưới dạng phân thì đôi, mỗi bài toán con có kích thước là n/2 => độ phức tạp thuật toán là O(nlogn)
Phân đoạn cân bằng: một bài toán con có kích thước n-k và bài toán kia có kích thước là k => độ phức tạp thuật toán là O(n)
6 Đánh giá thuật toán
Trang 96.1 Ưu điểm
Thuật toán có độ phức tạp nhỏ hơn các thuật toán sắp xếp đơn giản, tốc độ
xử lý tương đối nhanh Trong một số trường hợp, quicksort là thuật toán
có tốc độ tốt nhất
Thông dụng, được áp dụng nhiều trong lập trình, trong thư viện của các ngôn ngữ lập trình như C++, Java, C#
Có thể ứng dụng vào xử lý dữ liệu lớn
6.2 Nhược điểm
Thuật toán không có tính ổn định, không có tính thích ứng, dễ ảnh hưởng bởi dữ liệu đầu vào
Tốn không gian bộ nhớ hơn so với các thuật toán sắp xếp đơn giản
Tư tưởng và giải thuật khá phức tạp
Khó khăn trong việc lựa chọn phần tử làm chốt trong phân hoạch Việc lựa chọn có thể ảnh hưởng rất nhiều đến hiệu suất của thuật toán tuy nhiên
ta không thế đưa ra lựa chọn tối ưu nhất
7 Bài tập quick-sort
Ví dụ 1:dùng thuật toán Quick Sort sắp xếp day sau :
Chọn 7 làm pivot;
Lượt 1 : 7
Trang 10Dãy là : 5 2 4 6 7 9 10
LR Sau lượt 1 ta được 3 dãy mới là : 5 2 4 6;7;9 10
Ta thấy dãy 7 có 1 phần tử nên không phải sét,dãy 9 10 đã đúng yêu cầu nên không phải sét nữa
Chọn 5 làm pivot
Lượt 2 : 5
Đổi chỗ: 4 2 5 6 7 9 10
LR
Ta lại chia dãy 5 2 4 6 thành 3 dãy mới là : 4 2;5;6
Ta thấy dãy 5;6 có 1 phần tử thôi nên bỏ qua ta lại xét dãy 4 2
Chọn 4 làm pivot
Lượt 3: 4
2 5 6 7 9 10
L R
Đổi chỗ : 2 4 5 6 7 9 10
LR
Sau tất cả dãy được sắp xếp là : 2 4 5 6 7 9 10
Ví dụ 2 : sắp xếp dãy sau bằng thuật toán Quick Sort
11 33 6 7 22 9 1
Chọn 11 làm pivot
Lượt 1 :11
33 6 7 22 9 1
Đổi chỗ :1 33 6 7 22 9
Trang 11L R
Đổi chỗ :1 6 7 22 9 33
Đổi chỗ :1 9 6 7 22 33
L R Đổi chỗ : 1 9 6 7 11 22 33
LR
Ta được 3 dãy mới là : 1 9 6 7 ; 11 ; 22 33
Ta thấy được dãy 11 có 1 phần tử nên không phải sét nữa ,dãy 22 33 đã thỏa mãn yêu cầu nên cũng không phải sét nữa
Ta xét dãy 1 9 6 7
Chọn 1 làm pivot
Lượt 2:1
1 9 6 7 11 22 33
LR
Ta được 2 dãy mới là : 1 ; 9 6 7
Dãy 1 có 1 phần tử nên không phải xét ,dãy 9 6 7 ta chọn 9 làm pivot
Lượt 3:9
Đỏi chỗ :1 7 6 9 11 22 33
LR
Ta được 2 dãy mới là : 7 6 ; 9
Dãy 9 có 1 phần tử nên không phái xét ,ta xét dãy 7 6 chọn 7 làm pivot
Lượt 4:7
1 6 9 11 22 33
L R
Trang 12Đổi chỗ : 1 6 7 9 11 22 33
LR
Sau tất cả dãy được xắp xếp là : 1 6 7 9 11 22 33