快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
步骤为:
- 从数列中挑出一个元素,称为 "基准"(pivot),
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
在简单的伪代码中,此算法可以被表示为:
procedure quicksort(a, left, right) if right > left select a pivot value a[pivotIndex] pivotNewIndex := partition(a, left, right, pivotIndex) quicksort(a, left, pivotNewIndex-1) quicksort(a, pivotNewIndex+1, right)
对于待排序的输入顺序之所有排列情形的平均比较次数,可以借由解出以下递归关系式可以精确地算出来。
在这里,n-1 是分区所使用的比较次数。因为基准值是相当均匀地落在排列好的数列次序之任何地方,总和就是所有可能分区的平均。故,快速排序的平均时间复杂度为
C版本算法实现:
#include "stdio.h"
int partition(int data[], int left, int right)
{
#if 0
//双向扫描-这种做法当有相同数据时,会有死循环的情况(不建议使用)
int key = data[left];
int i = left+1;
int j = right;
while(i <= j)
{
//gcc编译器可能不支持这种写法,编译不会报错,但是运行会有问题
//while(data[i++] < key);
//while(data[j--] > key);
while(data[i] < key) ++i;
while(data[j] > key) --j;
if(i < j)
{
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
data[left] = data[j];
data[j] = key;
return j;
#endif
#if 0
//单向扫描
int key = data[left];
int temp = data[left];
data[left] = data[right];
data[right] = temp;
int i = left, j = left;
while(i < right)
{
if(data[i] <= key)
{
int _temp = data[i];
data[i] = data[j];
data[j] = _temp;
++j;
}
++i;
}
int __temp = data[right];
data[right] = data[j];
data[j] = __temp;
return j;
#endif
#if 1
//双向扫描(有相同元素不影响排序)
int key = data[left];
int i = left;
int j = right;
while(i < j)
{
while(i < j && data[j] >= key) --j;
if(i < j)
data[i++] = data[j];
while(i < j && data[i] <= key) ++i;
if(i < j)
data[j--] = data[i];
}
data[i] = key;
return i;
#endif
}
void quick_sort(int data[], int low, int high)
{
if(low < high)
{
int index = partition(data, low, high);
quick_sort(data, low, index-1);
quick_sort(data, index+1, high);
}
}
void print_data(int data[], int len)
{
int i = 0;
for(i = 0; i < len; ++i)
{
printf("%d ", data[i]);
}
printf("\n");
}
void main()
{
int data[] = {3,6,2,1,7,4,8,5};
int size = sizeof(data)/sizeof(int);
printf("before quick sort:\n");
print_data(data, size);
#if 1
printf("after quick sort:\n");
quick_sort(data, 0, size-1);
print_data(data, size);
#endif
}
lua版本算法实现:
local t = {5,1,3,6,3,4,2,3}
function partition(t, left, right)
------------
-- 单向扫描
local key = t[left]
local index = left
t[index],t[right] = t[right],t[index]
local i = left
while i < right do
if key >= t[i] then
t[index],t[i] = t[i],t[index]
index = index + 1
end
i = i + 1
end
t[right],t[index] = t[index],t[right]
return index;
------------
--
-- 双向扫描
local key = t[left]
local i = left
local j = right
while i < j do
while i < j and t[j] >= key do
j = j - 1
end
if i < j then
t[i] = t[j]
i = i + 1
end
while i < j and t[i] <= key do
i = i + 1
end
if i < j then
t[j] = t[i]
j = j - 1
end
end
--最后i,j相等
t[i] = key
return i
end
function quick_sort(t, low, high)
if low < high then
local index = partition(t, low, high)
quick_sort(t, low, index-1)
quick_sort(t, index+1, high)
end
end
print("---before quick sort---")
print(table.concat(t,' '))
print("---after quick sort---")
quick_sort(t, 1, #t)
print(table.concat(t,' '))