Thuật toán dựa trên kỹ thuật chia để trị,được đề xuất bởi C.A.R Hoare
Ý tưởng như sau:Sắp xếp dãy khóa k[1..n] thì có thể coi là sắp xếp đoạn từ chỉ số 1 tới chỉ số n
trong dãy khóa đó.
Nếu đoạn đó có ít hơn 2 khóa thì không cần làm gì cả,nếu đoạn đó có ít nhất 2 khóa,ta chọn một
khóa ngẫu nhiên nào đó làm chốt(pivot).Mọi khóa nhỏ hơn khóa chốt được xếp vào vị trí đứng
trước chốt,mọi khóa lớn hơn chốt được xếp vào vị trí sau chốt.Sau phép hoán chuyển như vậy thì
đoạn đang xét được chia làm 2 đoạn khác rỗng mà mọi khóa trong đoạn đầu đều <=chốt và mọi
khóa trong đoạn sau đều >=chốt.Vấn đề trở thành sắp xếp 2 đoạn mới được tạo ra (độ dài ngắn hơn
độ dài đoạn ban đầu ) bằng phương pháp tương tự (gọi đệ quy)
Độ phức tạp là O(n*lgn)
#include <stdio.h>
#include <stdlib.h>
#define MAX 10000
char *input="day_so.inp";
float a[MAX];
int N;
void quick_sort(int l,int r)
{
float tg;
float pivot;
int i,j;
if (l<r) //truong hop suy bien
{
i=l;j=r;
pivot=a[random(r-l+1)+l]; //Các bạn đặc biệt chú ý chỗ này
do
{
while (a[i]<pivot) i++;
while (a[j]>pivot) j--;
if (i<=j)
{
tg=a[i];
a[i]=a[j];
a[j]=tg;
i++;j--;
}
}
while (i<=j);
quick_sort(l,j);
quick_sort(i,r);
}
}
void main()
{
FILE *f;
int i;
/* Doc du lieu vao tu FILE */
f=fopen(input,"r");
fscanf(f,"%d",&N);
for (i=0;i<N;i++)
fscanf(f,"%f",&a[i]);
fclose(f);
quick_sort(0,N-1);
/* in ra ket qua */
printf("\n Mang sau khi da sap xep la:\n");
for (i=0;i<=N-1;i++) printf("%f\n",a[i]);
getch();
}
Các bạn lưu ý trong đoạn code của mình dùng "pivot=a[random(r-l+1)+l]",cái này rất nhiều sách
không đề cập tới,thông thường người ta chọn chốt là phần tử đầu dãy a[l] hoặc cuối dãy a[r]
nhưng với bộ dữ liệu đặc biệt sẽ làm tăng thời gian thực hiện chương trình,do đó mình chọn phần tử
bất kì làm chốt.
Nếu có bug các bạn post lên để mình fix nha.
Phép chọn pivot cần có độ phức tạp O(1).
Pivot tối ưu là median của mảng, tức là một giá trị sao cho một nửa số phần tử của mảng nhỏ hơn nó
và nửa còn lại lớn hơn hoặc bằng nó. Nhưng bởi vì không có cách nào O(1) để tính được median,
người ta chỉ có thể chọn pivot một cách hú họa và bất kể cách đó hú họa thế nào thì độ phức tạp của
Quicksort trong trường hợp xấu nhất vẫn là O(n^2), không thể giảm được nữa.
Do đó để giảm thời gian tính toán, nên chọn pivot theo những công thức rất đơn giản (chẳng hạn
như lấy phần tử đầu tiên của mảng, lấy trung bình cộng của phần tử đầu tiên và cuối cùng trong
mảng, hoặc lấy trung bình cộng của 4 phần tử trong mảng), không nên dùng công thức phức tạp
nhất là công thức chứa lời gọi hàm (random,...) thì càng không nên.
Quick sort là phương pháp đổi chỗ từng phần (partition exchange), đây là phương pháp rất hiệu quả,
rất thông dụng..
Nội dung của phương pháp này như sau:
Chọn một nút bất kỳ trong danh sách(Giả sử là nút đầu tiên) gọi là nút làm trục (pivot node), xác
định vị trí hợp lệ của nút này trong danh sách (gọi là vị trí pivot).
Tiếp theo chúng ta phân hoạch các nút còn lại trong danh sách cần sắp xếp sao cho từ vị trí 0 đến vị
trí pivot-1 đều có nội dung nhỏ hơn hoặc bằng nút làm trục, các nút từ vị trí pivot+1 đến n-1 đều có
nội dung lớn hơn nút làm trục.
Quá trình lại tiếp tục như thế với hai danh sahs con từ trị trí 0 đến vị trí pivot-1 và từ vị trí pivot+1
đến vị trí n-1, ... Sau cùng chúng ta sẽ được danh sách có thứ tự.
Mô tả giải thuật Quick sort
Code:
Giai thuat: QuickSort(nodes[], low, up)
Mo Ta; Giair thuat QuickSort, dung phuong phap de qui sawp xep va cac nut trong danh sach giua
hai vi tri
low va up
Du Lieu nhap:
- Danh sach cac nut chua sap xep (giua hai vi tri low va up)
- low va up
Du Lieu Xuat:
Danh sach cac nut (giua hai vi tri low va up) da duoc sap xep
Hanh dong
if(low >= up) // dieu kien dung
ket thuc giai thuat
if(low < up)
- Phan hoach: partition(nodes[], low, up, pivot)
+ partition phan danh sach thanh ba phan:
* nut lam truc: nodes[low] tro thanh nodes[pivot]
* danh sach con 1: nodes[i] <= nodes[pivot]
(voi i < pivot)
* danh sach con 2: nodes[i] > nodes[pivot]
(voi i > pivot)
- Goi de qui: QuickSort(nodes[], low, pivot-1)
- Goi de qui: QuickSort(nodes[], pivot+1, up)
Ket Thuc
Giải thuật Partion
vấn đề tiếp theo là giải thuật partition giúp phân danh sách làm ba phần:
• Nút đầu (nút làm trục) đặt ở vị trí pivot
• Danh sách con 1 từ vị trí low đến pivot-1 có nội dung nhỏ hơn hay bằng nút làm trục.
• Danh sách con 2 từ vị trí low đến pivot+1 có nội dung lớn hơn hay bằng nút làm trục.
Người ta xử lý giải thuật partition theo mô tả như sau:
1. Chọn nodes[low] (nút đầu tiền) là nút làm trục.
2. Quét danh sách theo hai hướng để đổi chỗ các cặp nút "sai" vị trí, nơi gặp nhau của hai hướng
quét chính là vị trí pivot:
Quét từ low lên: con trỏ l xuất phát từ vị trí low và tăng dần lên, dừng lại khi gặp nút có nội dung
lớn hơn nút làm trục, ghi nhận vị trí l lúc này.
• Quét từ up xuống: con trỏ u xuất phát từ vị trí up và giảm dần xuống, dừng lại khi gặp nút có
nội dung nhỏ hơn hay bằng nút làm trục, ghi nhận vị trí u lúc này.
• Đổi chỗ hai nút tại vi trí l và u.
• Cứ tiếp tục quét theo hai hướng và đổi chỗ các cặp nút "sai" vị trí, quá trình này dừng lại khi
l = u (hai hướng quét gặp nhau), nợi gặp nhau chính là vị trí pivot (pivot = u = l).
3. Đổi chỗ hai nút tại vị trí low (nút làm trục) và nút tại vị trí pivot.
Sau đây là giải thuật partition
Code:
Giai Thuat: partition(nodes[], low, up, pivot)
Mo Ta: Giai thuat partition, phan danh sach thanh 3 phan ...
Du Lieu Nhap:
Danh sach cac nut trong khoang vi tri tu low den up.
Du Lieu Xuat:
Dua nut lam truc ve vi tri pivot, doi cho cac nut trong danh sach
sao cho cac nut co noi dung nho hon hay bang nut lam truc duoc
bo tri truoc nut lam truc, cac nut co noi dung lon hon nut lam
truc duoc bo tri sau nut lam truc.
Hanh Dong
1. Chon nodes[low] la nut lam truc
Gan: l = low;
u = up;
2. while(l < u) // vong lap xu ly hai huong quet
{
Quet huong tu low len, dung lai khi gap nut lon hon nut lam truc,
ghi nhan vi tri l luc nay
Quet huong tu up xuong, dung lai khi gap nut nho hon hay bang
nut lam truc, ghi nhan vi tri u luc nay
Doi cho hai nut tai hai vi tri l va u
}
3. Doi cho hai nut tai hai vi tri low va u (luc nay pivot = u)
Ket Thuc
Cài đặt giải thuật QuickSort
Sau đây là hàm QuickSort() dùng phương pháp đệ qui, hàm này có gọi hàm partition() để phân
hoạch danh sách con thành 3 phần.
Hàm QuickSort()
void QuickSort(int nodes[], int low, int up)
{
int pivot;
if(low >= up) // dieu kien dung
return;
if(low < up)
{
partition(nodes, low, up, &pivot);
QuickSort(nodes, low, pivot - 1);
QuickSort(nodes, pivot + 1, up);
}
}
Hàm partition():
void partition(int nodes [], int low, int up, int *pivot)
{
int nuttruc, l, u, temp;
nuttruc = nodes[low]; // Chon nut dau lam nut truoc
l = low;
u = up;
while(l < u)
{
while(nodes[l] <= nuttruc && l < up)
l++;
while(nodes[u] > nuttruc)
u--;
if(l < u)
{
// doi cho cap nut sai vi tri
temp = nodes[l];
nodes[l] = nodes[u];
nodes[u] = temp;
}
}
// doi cho hia nut tai low va u (luc nay u la vi tri nut truc)
nodes[low] = nodes[u];
nodes[u] = nuttruc;
*pivot = u;
}
Nhận xét, so sánh
• Quick Sort phức tạp hơn Bubble Sort nhưng hiệu quả hơn.
• Quick Sort thích hợp cho danh sách ban đầu chưa có thứ tự.
• Quick Sort kém hiệu quả khi danh sách ban đầu gần có thứ tự. Đặc biệt với danh sách dã có
thứ tự (lớn dần hoặc nhở dần) lại là trường hợp xấu nhất của giải thuật Quick Sort.
Chương trình minh họa sắp xếp một mảng kiểu int
#include <iostream.h>
#include <conio.h>
void partition(int nodes [], int low, int up, int *pivot);
void QuickSort(int nodes[], int low, int up)
{
int pivot;
if(low >= up) // dieu kien dung
return;
if(low < up)
{
partition(nodes, low, up, &pivot);