0
点赞
收藏
分享

微信扫一扫

快速排序的优化

其生 2022-04-23 阅读 45
c语言

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作为进行插入排序的触发条件,那到底多大才能让时间复杂度减少最多呢,我也不清楚。

举报

相关推荐

0 条评论