1. 三数取中
对于快速排序法来说什么情况时间复杂度是最低的?
当每一次的基准数key都位于中间时,可以将此看作二叉树,则该二叉树的高度就是log(N);
每一层都有o(N),合计就是(N*logN)
什么情况时间复杂度最高呢?
当key位于两边时,时间复杂度就是N + N-1 +……+ 1 约等于 O(N*N)
所以当排序的数组是有序或者接近有序时,快速排序的时间复杂度会很大,甚至会有以下情况:
任何数都没有显示出来,并且还会有栈溢出问题。
这是为什么呢?
因为我们的基准数默认选择了最左边或者最右边,但这就会导致最后基准数还是在最左边或者最右边。
那我们应该怎么避免我们的基准数key最终位置在两边呢?
我们的主角 - 三数取中登场了
三数分别是:最左边、最右边、中间,我们在这三个数当中选择大小是中间的数。
并让它成为我们的基准数,那我们的问题就迎刃而解了。
代码如下:
//三数取中 给快排增加了无限可能
int GetMidIndex(int*a,int left,int right){
int mid =left+(right-left)>>1;
if(a[left]<a[mid]){
if(a[mid]<a[right]){
return mid;
}
else if(a[left]>a[right]){
return left;
}
else{
return right;
}
}else{
if(a[mid]>a[right]){
return mid;
}
else if(a[right]>a[left]){
return left;
}
else{
return right;
}
}
}
//这里我们选择基准数分组中的挖坑法,其他的方法也使用三数取中
int Partition1(int*a,int left,int right){
int midi = GetMidIndex(a,left,right); //得到中位数
swap(a,left,midi); //与left交换
int key=a[left]; //此left非left,是我们的mini了,其他就是正常操作
//挖坑
int pit=left;
while(left<right){
while(left<right&&a[right]>=key){
right--;
}
if(left<right){
a[pit]=a[right];
pit=right;
}
while(left<right&&a[left]<=key){
left++;
}
if(left<right){
a[pit]=a[left];
pit=left;
}
}
a[pit]=key;
return pit;
}
2. 小区间插入
快速排序的操作离不开递归,又因为快速排序递归图类似于二叉树,那我们可以发现在越往下的时候,递归的次数就逐渐上升,时间复杂度也不断增加,那我们如何减少这段过程的时间复杂度呢?
当区间大小在一定范围里,我们可以使用插入排序来替换递归分组,从而提升我们的时间复杂度。
代码实现如下:
这里不仅仅是传入a,必须是a+left,才能保证插入排序作用的是想要的区间。
时间复杂度也降低了不少,虽然也不是很多,但是有就是好。
这里我们选择小于13作为进行插入排序的触发条件,那到底多大才能让时间复杂度减少最多呢,我也不清楚。