快速排序是由東尼·霍爾所發(fā)展的一種排序算法。在平均狀況下恳邀,排序 n 個(gè)項(xiàng)目要 Ο(nlogn) 次比較。在最壞狀況下則需要 Ο(n2) 次比較灶轰,但這種狀況并不常見(jiàn)谣沸。事實(shí)上,快速排序通常明顯比其他 Ο(nlogn) 算法更快笋颤,因?yàn)樗膬?nèi)部循環(huán)(inner loop)可以在大部分的架構(gòu)上很有效率地被實(shí)現(xiàn)出來(lái)鳄抒。
快速排序使用分治法(Divide and conquer)策略來(lái)把一個(gè)串行(list)分為兩個(gè)子串行(sub-lists)。
快速排序又是一種分而治之思想在排序算法上的典型應(yīng)用椰弊。本質(zhì)上來(lái)看许溅,快速排序應(yīng)該算是在冒泡排序基礎(chǔ)上的遞歸分治法。
快速排序的名字起的是簡(jiǎn)單粗暴秉版,因?yàn)橐宦?tīng)到這個(gè)名字你就知道它存在的意義贤重,就是快,而且效率高清焕!它是處理大數(shù)據(jù)最快的排序算法之一了并蝗。雖然 Worst Case 的時(shí)間復(fù)雜度達(dá)到了 O(n2)祭犯,但是人家就是優(yōu)秀,在大多數(shù)情況下都比平均時(shí)間復(fù)雜度為 O(n logn) 的排序算法表現(xiàn)要更好滚停,可是這是為什么呢沃粗,我也不知道。好在我的強(qiáng)迫癥又犯了键畴,查了 N 多資料終于在《算法藝術(shù)與信息學(xué)競(jìng)賽》上找到了滿意的答案:
快速排序的最壞運(yùn)行情況是 O(n2)最盅,比如說(shuō)順序數(shù)列的快排。但它的平攤期望時(shí)間是 O(nlogn)起惕,且 O(nlogn) 記號(hào)中隱含的常數(shù)因子很小涡贱,比復(fù)雜度穩(wěn)定等于 O(nlogn) 的歸并排序要小很多。所以惹想,對(duì)絕大多數(shù)順序性較弱的隨機(jī)數(shù)列而言问词,快速排序總是優(yōu)于歸并排序。
- 算法步驟
從數(shù)列中挑出一個(gè)元素嘀粱,稱為 "基準(zhǔn)"(pivot);
重新排序數(shù)列激挪,所有元素比基準(zhǔn)值小的擺放在基準(zhǔn)前面,所有元素比基準(zhǔn)值大的擺在基準(zhǔn)的后面(相同的數(shù)可以到任一邊)锋叨。在這個(gè)分區(qū)退出之后垄分,該基準(zhǔn)就處于數(shù)列的中間位置。這個(gè)稱為分區(qū)(partition)操作悲柱;
遞歸地(recursive)把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序锋喜; - 動(dòng)圖演示
typedef struct _Range {
int start, end;
} Range;
Range new_Range(int s, int e) {
Range r;
r.start = s;
r.end = e;
return r;
}
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
void quick_sort(int arr[], const int len) {
if (len <= 0)
return; // 避免len等於負(fù)值時(shí)引發(fā)段錯(cuò)誤(Segment Fault)
// r[]模擬列表,p為數(shù)量,r[p++]為push,r[--p]為pop且取得元素
Range r[len];
int p = 0;
r[p++] = new_Range(0, len - 1);
while (p) {
Range range = r[--p];
if (range.start >= range.end)
continue;
int mid = arr[(range.start + range.end) / 2]; // 選取中間點(diǎn)為基準(zhǔn)點(diǎn)
int left = range.start, right = range.end;
do {
while (arr[left] < mid) ++left; // 檢測(cè)基準(zhǔn)點(diǎn)左側(cè)是否符合要求
while (arr[right] > mid) --right; //檢測(cè)基準(zhǔn)點(diǎn)右側(cè)是否符合要求
if (left <= right) {
swap(&arr[left], &arr[right]);
left++;
right--; // 移動(dòng)指針以繼續(xù)
}
} while (left <= right);
if (range.start < right) r[p++] = new_Range(range.start, right);
if (range.end > left) r[p++] = new_Range(left, range.end);
}
}
遞歸法
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
void quick_sort_recursive(int arr[], int start, int end) {
if (start >= end)
return;
int mid = arr[end];
int left = start, right = end - 1;
while (left < right) {
while (arr[left] < mid && left < right)
left++;
while (arr[right] >= mid && left < right)
right--;
swap(&arr[left], &arr[right]);
}
if (arr[left] >= arr[end])
swap(&arr[left], &arr[end]);
else
left++;
if (left)
quick_sort_recursive(arr, start, left - 1);
quick_sort_recursive(arr, left + 1, end);
}
void quick_sort(int arr[], int len) {
quick_sort_recursive(arr, 0, len - 1);
}