一、特点
当划分均匀时快速排序的时间复杂度是O(nlogn),空间复杂度是O(logn)。
当划分完全不均匀时时间复杂度是O(n²),空间复杂度是O(n)。
二、思想
快排是对冒泡的进一步改进,它的思想是分治和递归。通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
三、代码及结果
#include<stdio.h>
//查找中轴,并把数据按中轴分为两部分,一边比中轴大,一边比中轴小
int findmid(int *arr,int left,int right)
{
int pivot=arr[left];//将数组第一个数定义为中轴
while(left < right)//如果left=right时循环结束,这是的left或right便是中轴的位置,将pivot赋值给这个位置。并按这个位置的两边,分成两个数组开始递归,直到left=right,完成排序。
{
while(left < right && arr[right]>=pivot)
{
right--;
}
if(left < right)
{
arr[left++]=arr[right];//注意这是把arr[right]的值赋给arr[left]然后left++,因为arr[left]的值已经作为中轴赋值给pivot,所以覆盖掉没有关系,并且只有覆盖掉才能找到最后left=right时的位置也就是中轴的位置,这时再将pivot复制到这个位置。
}
while(left < right && arr[left]<=pivot)
{
left++;
}
if(left < right)
{
arr[right--]=arr[left];//
}
}
arr[left]=pivot;
return left;
}
//快排
void quickSort(int *arr,int left,int right)
{
if(left<right)
{
int mid = findmid(arr,left,right);
quickSort(arr,mid+1,right);//右递归
quickSort(arr,left,mid-1);//左递归
}
}
int main()
{
int arr[] = {5,3,2,5,4,7,6,8,2,2};
int len=sizeof(arr)/sizeof(arr[1]);
quickSort(arr,0,len-1);
for(int i=0;i<len;i++)
{
printf("%d",arr[i]);
}
printf("\n");
}
四、优化
1、两种影响排序的因素
A.划分不均匀
对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。如果输入序列有序(当然正常情况算法应该判断当序列有序时直接返回),我们选最左边元素做枢轴点,即划分极度不均匀时,不仅时间复杂度变成O(n²),递归深度也变成了n,这样很容易令栈内存溢出。所以要尽量避免这种情况。
B.当有大量重复元素时
每趟排序对每个子序列只产生一个有序位置, 所以对数组中相等元素的处理效率不是很高。如果有大量重复元素,实际上快排做了很多无用工作。由于划分函数的特点,对于一个每个元素都完全相同的一个序列来讲,快速排序也会退化到 O(n^2)。
2、三数取中
3、随机选取基准
4、插入排序,对于很小和部分有序的数组,快排不如插排好
5、在一次分割结束后,可以把与Key相等的元素聚在一起,继续下次分割时,不用再对与key相等元素分割
代码
void QSort(int arr[],int low,int high)
{
int first = low;
int last = high;
int left = low;
int right = high;
int leftLen = 0; //用来统计左边与key相等的元素的个数
int rightLen = 0; //统计右边与key相等的元素的个数
if (high - low + 1 < 10)
{
InsertSort(arr,low,high); //数据量少,就用插入排序
return;
}
//一次分割
int key = SelectPivotMedianOfThree(arr,low,high);//使用三数取中法选择枢轴
while(low < high)
{
while(high > low && arr[high] >= key)
{
if (arr[high] == key)//处理相等元素
{
swap(arr[right],arr[high]); //把右边与key元素相等的聚集的右端
right--;
rightLen++;
}
high--;
}
arr[low] = arr[high];
while(high > low && arr[low] <= key)
{
if (arr[low] == key) //把左边与key元素相等的聚集数组的左端
{
swap(arr[left],arr[low]);
left++;
leftLen++;
}
low++;
}
arr[high] = arr[low];
}
arr[low] = key;
//一次快排结束
//把与枢轴key相同的元素移到枢轴最终位置周围
int i = low - 1; //轴的左边
int j = first;
while(j < left && arr[i] != key)
{
swap(arr[i],arr[j]); //此时,把数组左端与key相等的数据换到key的左边
i--;
j++;
}
i = low + 1; //轴的右边
j = last;
while(j > right && arr[i] != key)
{
swap(arr[i],arr[j]); //此时,把数组右端与key相等的数据换到,key右边
i++;
j--;
}
partition(arr,first,low - 1 - leftLen);
partition(arr,low + 1 + rightLen,last);
}