基本思想
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。
算法步骤
算法步骤简述如下:
- 选择一个基准值(pivot)(选择方法很多,可以固体选某个值,比如第一个或最后一个或中间值,或者是三数取中法等);
- 将比基准值(pivot)小的数值划分到基准值左边,构成左子串列,将比基准值(pivot)大的数值划分到基准值右边,构成右子串列;
- 分别对左子串列和右子串列递归地作上述两个步骤;
- 直到左子串列或右子串列只剩一个值或者为空。
基准值的选取
上面的第一步基准值的选择对快速排序的效率有很大关系。基准值(pivot)的选择办法有下面几种:
- 固定位置:第一个,最后一个或中间值;
- 随机选取:用随机函数随机选取一个;
- 三数取中:去第一个,中间值和最后一个数的平均值。
固定位置和随机选取的方法容易造成一种极端,如果选取的那个数刚好是最小值或最大值,比如数组是有序的,就会导致一个很差的分割,是左子串或右子串列为0,而且随机选取过程还会有额外的时间开销。所以都是不可取的。三数取中的办法就避免了上面的情况。
时间空间复杂度
快速排序的时间性能跟递归的深度有关,而空间复杂度跟递归造成的栈空间使用有关。最好的情况是,选取的基准值刚好是中位数,刚好将数据等分成2个子串,递归树也就是平衡的。递归调用需要 log2n 次,空间复杂度为 O(logn) ,时间复杂度为 O(nlogn) ;最坏的情况就是待排序的数据是有序的,正序或者逆序。递归需要 n−1 次,空间复杂度则需要 O(n) 时间复杂度为 O(n2)
代码实现
#define MAX_LENGTH_INSERT_SORT 7 // 数组长度阈值
int Partition(int arr[], int Left, int Right)
{
/* 三数取中*/
int mid = Left + (Right - Left) / 2;
if(arr[Left] > arr[Right])
swap(arr[Left],arr[Right]);
if(arr[mid] > arr[Right])
swap(arr[mid], arr[Right]);
if(arr[mid] > arr[Left])
swap(arr[mid], arr[Left]);
int pivot_key = arr[Left];
while(Left < Right)
{
while(Left < Right && arr[Right] >= pivot_key )
Right --;
arr[Left] = arr[Right];
while(Left < Right && arr[Left] <= pivot_key)
Left ++;
arr[Right] = arr[Left];
}
arr[Left] = pivot_key;
return Left;
}
void QSort(int arr[], int Left, int Right)
{
int pivot_index;
/*当Right - Left 大于 MAX_LENGTH_INSERT_SORT 采用 快排,否则使用直接插入*/
if(Right - Left > MAX_LENGTH_INSERT_SORT)
{
pivot_index = Partition(arr, Left, Right);
QSort(arr, Left, pivot_index - 1);
QSort(arr, pivot_index + 1, Right);
}
else
Insert_sort(arr, Right - Left + 1); // 直接插入法是简单排序当中性能最好的
}
void Quick_sort(int arr[], int len)
{
QSort(arr, 0, len - 1);
}
测试程序
#include <iostream>
#include <cstring>
#include <ctime>
#include <cmath>
using namespace std;
#define ArraySize 100000
void swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
void Bubble_sort(int arr[], int len)
{
for(int i = 0; i < len; i ++)
{
for(int j = i + 1; j < len; j ++)
if(arr[i] > arr[j])
swap(arr[i], arr[j]);
}
}
void Bubble_sort1(int arr[], int len)
{
for(int i = 0; i < len; i ++)
{
for(int j = len - 1; j >= i; j --)
{
if(arr[i] > arr[j])
swap(arr[i], arr[j]);
}
}
}
void Bubble_sort2(int arr[], int len)
{
bool flag = true;
while(flag)
{
flag = false;
for(int i = 0; i < len; i ++)
for(int j = len - 1; j >= i; j --)
if(arr[i] > arr[j])
swap(arr[i], arr[j]);
}
}
void Slect_sort(int arr[], int len)
{
for(int i = 0; i < len; i ++)
{
int min_index = i ;
for(int j = i + 1; j < len; j ++)
{
if(arr[min_index] > arr[j])
min_index = j;
}
if(i != min_index)
swap(arr[i],arr[min_index]);
}
}
void Insert_sort(int arr[], int len)
{
for(int i= 1; i < len; i ++)
{
int key = arr[i];
int j = i;
while(j && arr[j - 1] > key)
{
arr[j] = arr[j - 1];
j --;
}
arr[j] = key;
}
}
void Shell_sort(int arr[], int len)
{
int increment = len / 2;
while(increment)
{
for(int i = increment; i < len; i ++)
{
int key = arr[i];
/*int j ;
for(j = i; j >= increment; j -= increment)
{
if(arr[j-increment] > key )
arr[j] = arr[j-increment];
else
break;
}*/
int j = i;
while(j >= increment && arr[j-increment] > key)
{
arr[j] = arr[j-increment];
j -= increment;
}
arr[j] = key;
}
increment /= 2;
}
}
void Shell_sort1(int arr[], int len)
{
int increment = 0;
for(increment = len/2; increment > 0; increment /=2)
{
for(int i = increment; i < len; i++)
{
int key = arr[i];
int j = 0;
for(j = i; j >= increment; j -=increment)
{
if(arr[j-increment] > key)
arr[j] = arr[j-increment];
else
break;
}
arr[j] = key;
}
}
}
void Shell_sort2(int arr[], int len)
{
int index = log( 2*len + 1) / log(3.0);
//cout << index << endl;
int increment = ( pow(3.0, index) - 1 ) / 2;
//cout << increment << endl;
while(increment)
{
for(int i = increment; i < len; i ++)
{
int key = arr[i];
/*int j ;
for(j = i; j >= increment; j -= increment)
{
if(arr[j-increment] > key )
arr[j] = arr[j-increment];
else
break;
}*/
int j = i;
while(j >= increment && arr[j-increment] > key)
{
arr[j] = arr[j-increment];
j -= increment;
}
arr[j] = key;
}
index -= 1;
increment = ( pow(3.0, index) - 1 ) / 2;
}
}
void Heap_adjust(int arr[], int index, int len)
{
while(true)
{
int iMax = index;
int iLeft = 2 * index + 1;
int iRight = 2 * index + 2;
if(iLeft < len && arr[index] < arr[iLeft])
iMax = iLeft;
if(iRight < len && arr[index] < arr[iRight])
iMax = iRight;
if(iMax != index)
{
swap(arr[index], arr[iMax]);
index = iMax;
}
else
break;
}
}
void Heap_adjust2(int arr[], int index, int len)
{
int iMax = index;
int iLeft = 2 * index + 1;
int iRight = 2 * index + 2;
if(iLeft < len && arr[index] < arr[iLeft])
iMax = iLeft;
if(iRight < len && arr[index] < arr[iRight])
iMax = iRight;
if(iMax != index)
{
swap(arr[index], arr[iMax]);
Heap_adjust2(arr, iMax, len);
}
}
void Build_maxheap(int arr[], int len)
{
for(int i = len / 2; i >= 0; i --)
{
Heap_adjust(arr, i , len);
}
}
void Heap_Sort(int arr[], int len)
{
Build_maxheap(arr, len);
for(int i = len - 1; i > 0; i --)
{
swap(arr[0], arr[i]);
Heap_adjust(arr, 0, i);
}
}
void Print_array(int arr[], int len)
{
for(int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
void merge_array(int arr[], int tmp[], int Left, int mid, int Right)
{
assert(arr && Left >= 0 && Left <= mid && mid <= Right);
int i = Left;
int j = mid + 1;
int index = 0;
while(i <= mid && j <= Right)
{
if(arr[i] <= arr[j])
tmp[index++] = arr[i++];
else
tmp[index++] = arr[j++];
}
while(i <= mid)
tmp[index++] = arr[i++];
while(j <= Right)
tmp[index++] = arr[j++];
memcpy(arr + Left, tmp, (Right - Left + 1) * sizeof(int));
}
void merge_sort(int arr[], int tmp[], int Left, int Right)
{
assert(arr && Left >= 0);
int mid;
if(Left < Right)
{
mid = (Left + Right) / 2;
merge_sort(arr, tmp, Left, mid);
merge_sort(arr, tmp, mid + 1, Right);
merge_array(arr, tmp, Left, mid, Right);
}
}
void Merge_Sort(int arr[], int len)
{
assert(arr && len);
int *tmp_arr;
//tmp_arr = (int *)malloc(len * sizeof(int));
tmp_arr = new int[len];
merge_sort(arr, tmp_arr, 0, len - 1);
delete[] tmp_arr;
}
#define MAX_LENGTH_INSERT_SORT 7 // 数组长度阈值
int Partition(int arr[], int Left, int Right)
{
/* 三数取中*/
int mid = Left + (Right - Left) / 2;
if(arr[Left] > arr[Right])
swap(arr[Left],arr[Right]);
if(arr[mid] > arr[Right])
swap(arr[mid], arr[Right]);
if(arr[mid] > arr[Left])
swap(arr[mid], arr[Left]);
int pivot_key = arr[Left];
while(Left < Right)
{
while(Left < Right && arr[Right] >= pivot_key )
Right --;
arr[Left] = arr[Right];
while(Left < Right && arr[Left] <= pivot_key)
Left ++;
arr[Right] = arr[Left];
}
arr[Left] = pivot_key;
return Left;
}
void QSort(int arr[], int Left, int Right)
{
int pivot_index;
/*当Right - Left 大于 MAX_LENGTH_INSERT_SORT 采用 快排,否则使用直接插入*/
if(Right - Left > MAX_LENGTH_INSERT_SORT)
{
pivot_index = Partition(arr, Left, Right);
QSort(arr, Left, pivot_index - 1);
QSort(arr, pivot_index + 1, Right);
}
else
Insert_sort(arr, Right - Left + 1); // 直接插入法是简单排序当中性能最好的
}
void Quick_sort(int arr[], int len)
{
QSort(arr, 0, len - 1);
}
int main(int argc, char const *argv[])
{
/* code */
int Array[ArraySize];
int Array1[ArraySize];
int Array2[ArraySize];
int Array3[ArraySize];
time_t begin , end;
srand(time(NULL));
for(int i = 0; i < ArraySize; i ++)
{
Array[i] = rand()%ArraySize;
//cout << Array[i] << " ";
}
memcpy(Array1, Array, ArraySize * sizeof(Array1[0]));
memcpy(Array2, Array, ArraySize * sizeof(Array2[0]));
memcpy(Array3, Array, ArraySize * sizeof(Array2[0]));
// Print_array(Array, ArraySize);
/* begin = clock();
Bubble_sort2(Array, ArraySize);
end = clock();
cout << "Bubble_sort runtime: " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;
begin = clock();
Slect_sort(Array1, ArraySize);
end = clock();
cout << "Slect_sort runtime: " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;
begin = clock();
Insert_sort(Array2, ArraySize);
end = clock();
cout << "Insert_sort runtime: " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;*/
begin = clock();
Shell_sort2(Array, ArraySize);
end = clock();
cout << "Shell_sort2 runtime: " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;
begin = clock();
Heap_Sort(Array1, ArraySize);
end = clock();
cout << "Heap_Sort runtime: " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;
begin = clock();
Merge_Sort(Array2, ArraySize);
end = clock();
cout << "Merge_Sort runtime: " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;
//Print_array(Array2, ArraySize);
begin = clock();
Quick_sort(Array3, ArraySize);
end = clock();
cout << "Quick_sort runtime: " << double(end - begin) / CLOCKS_PER_SEC << "s" << endl;
//Print_array(Array3, ArraySize);
return 0;
}
运行结果如下:
Shell_sort2 runtime: 0.028s
Heap_Sort runtime: 0.005s
Merge_Sort runtime: 0.013s
Quick_sort runtime: 0.011s
参考文献
知无涯之std::sort源码剖析
http://feihu.me/blog/2014/sgi-std-sort/