快速排序是一种性能较好的算法,有最好的平均性能。由冒泡排序算法改进,采用分治思想:
1.将整个区间划分两个子区间p1, p2和元素k, 使得p1区间的所有元素均小于k, p2区间的所有元素均大于或等于k(这里k成为枢轴元素),
2.再对p1和p2两个区间分别重复步骤1, 如此递归
3.由于步骤1是就地排序,直到最后每个区间的元素个数为1, 这样整个区间就有序了。
实现步骤1的函数
int Partion(int *p, int low, int high)
这个函数根据选择的枢轴元素不同, 实现也不同,常见的有:
1.第一个元素选为枢轴元素int Partion(int *p, int low, int high)
{
while(high>low)
{
while(high>low && p[high]>p[low]) //#1
{
--high;
}
swap(p+high, p+low);
for(high>low && p[high]>=p[low]) //注意这里和#1处必须保证至少有一个地方取等号, 否则遇到相等的元素时会出现死循环, 函数不能退出。
{
++low;
}
swap(p+high, p+low);
}
return low;
}
2.最后一个元素选为枢轴元素
int Partion(int *p, int low, int high)
{
int i, j = low;
for(i=low; i<high-1; ++i)
{
if(p[i]<=p[high])
{
if(i!=j) // 避免不必要的交换
{
swap(p+i, p+j);
}
++j;
}
}
swap(p+j, p+high);
return j;
}
当序列有序的时候, 不管是选第1个还是最后一个元素为枢轴元素, 都会划分一个长度为1, 一个长度为N-2的两个子区间,子区间长度相差较大,所有子区间组成的树不平衡,造成排序时间复杂度为O(n^2),效率较低。可以采用随机化的方法选择枢轴元素:
int partion(int *p, int low, int high)
{
int k = random() ;
swap(p+k, p+low); // 或者swap(p+k, p+high);
// 随机选择一个元素与第一个元素或者最后一个元素交换, 作为枢轴元素
//
// 接下来与第一个或者最后一个元素作为枢轴元素相同
....
}
这样效率可以达到O(n*logn)。
通常快速排序具有最好的平均性能,时间复杂度为O(n*logn), 常数因子较其他同类算法(时间复杂度为O(n*logn)的算法)小。可是当区间元素有序(正序或者逆序)的时候,快速排序就蜕变成时间复杂度为O(n^2)的算法了。
代码实现
1.递归实现
void static qsort(int *p, int low, int high)
{
if(low < high)
{
int pivot = partion(p, low, high);
qsort(p, low, pivot-1);
qsort(p, pivot+1, high);
}
}
2. 非递归实现
void qsort(int *p, int low, int high)
if(low < high)
{
Stack stack;
init_stack(&stack);
int mid = partion(p, low, high);
if(mid-1 >= 0)
{
push_stack(&stack, low);
push_stack(&stack, mid-1);
}
if(mid+1 <= high)
{
push_stack(&stack, mid+1);
push_stack(&stack, high);
}
while(!empty_statck(&stack))
{
int m, n;
if(pop_stack(&stack, &n) && pop_stack(&stack, &m))
{
mid = partion(p, m, n);
if(mid-1 >= m)
{
push_stack(&stack, m);
push_stack(&stack, mid-1);
}
if(mid+1 <= n)
{
push_stack(&stack, mid+1);
push_stack(&stack, n);
}
}
}
}
}