快速排序
快速排序是一种基于分治法的排序方法,每一趟快排选择序列中任一个元素作为枢轴(pivot)(通常选第一个元素),将序列中比枢轴小的元素都移到枢轴前边,比枢轴大的元素都移到枢轴后边。
分治法:
第一步首先将原问题分解成若干个子问题,这个子问题只是原问题较小规模的实例。
第二步将解决这些子问题,递归地求解各子问题。递归的边界就是问题规模足够小的时候,可以直接求解。
算法性能
时间复杂度:最好情况下时间复杂度为O(nlogn) ,待排序序列越无序,算法效率越高。
最坏情况下时间复杂度为O(n2),待排序序列越有序,算法效率越低。
递归复杂度的表达式:T(n) = aT(n/b) + f(n)
n/b是子问题的大小,比如快排每次划分子问题之后的大小,a是子问题大小的数量,f(n)是分解问题和合并问题所需要的时间
1)快速排序最优的情况就是每一次Partition取到的元素都刚好平分整个数组 即a=b=2 f(n)=O(n)=Θ(n) T(n)=O(nlogn)
2)快速排序最坏的情况就是每一次Partition取到的元素在一端,即T(n)=T(n-1)+T(0)+f(n)=T(n-1)+f(n)=T(n-2)+f(n)+f(n-1)=……=f(1)+f(2)+……+f(n)(等差数列)=O(n2)
空间复杂度:由于快速排序是递归的,需要借助一个递归工作栈来保存每一层递归调用的必要信息,其容量应与递归调用的最大深度一致。
最好情况下为 ⌈log2(n+1)⌉(每次partition都很均匀)递归树的深度O(logn)
最坏情况下,因为要进行n-1次递归调用,所以栈的深度为O(n);
稳定性:快速排序是不稳定的,是因为存在交换关键字。
实现代码
/**
* 快速排序
*/
#include <stdio.h>
// 数组长度
#define LENGTH(array) ( (sizeof(array)) / (sizeof(array[0])) )
/*
* 快速排序
*
* 参数说明:
* arr -- 待排序的数组
* low -- 数组的左边界(例如,从起始位置开始排序,则low=0)
* high -- 数组的右边界(例如,排序截至到数组末尾,则high=arr.length-1)
*/
void quickSort(int arr[], int low, int high)
{
int i=low,j=high,temp;
if (low < high)
{
temp = arr[i];//暂存low里面的关键字
while (i < j)
{
while(i < j && arr[j] > temp)
j--; // 从右向左找第一个小于temp的数
if(i < j)
arr[i++] = arr[j];
while(i < j && arr[i] < temp)
i++; // 从左向右找第一个大于temp的数
if(i < j)
arr[j--] = arr[i];
}
arr[i] = temp;
quickSort(arr, low, i-1); /* 递归调用 */
quickSort(arr, i+1, high); /* 递归调用 */
}
}
void main()
{
int i;
int arr[] = {30,40,88,554,60,10,20,50};
int ilen = LENGTH(arr);
printf("排序前:");
for (i=0; i<ilen; i++)
printf("%d ", arr[i]);
printf("\n");
quickSort(arr, 0, ilen-1);
printf("排序后:");
for (i=0; i<ilen; i++)
printf("%d ", arr[i]);
printf("\n");
}