快速排序的优化方法——代码实现、时间对比

前言

在网上看到了快速排序的优化方法,但是没有完整的代码,于是自己花时间实现了代码。全部代码在尾部链接。

快速排序的目标数组分为4种:随机数组,降序数组,升序数组,重复数组。数组的长度为一百万。

随机数组:确保没有重复的元素值

降序数组:随机数组的降序排序

升序数组:随机数组的升序排序

重复数组:全部为10的数组

1、基本快速排序

基本的快速排序,就是选取开始位置或者末尾位置的元素作为基准

运行时间:

(时间单位:ms)随机数组降序数组升序数组重复数组
基本快速排序240149847015181801518230

分析:

降序、升序、重复数组三者的时间复杂度理论上为n*n。 

2、随机基准

说明:

使用随机函数,选取范围内随机的一个位置的元素值,作为基准

核心代码:

srand((unsigned)time(NULL));
int loc=rand()%(end-start)+start;
swap(nums[start],nums[loc]);
int baseValue = nums[start];

运行时间:

(时间单位:ms)随机数组降序数组升序数组重复数组
基本快速排序240149847015181801518230
随机基准9806507901520030

分析:

1、使用随机基准,那么对于降序数组和升序数组,每次的划分,就可能不是n-1和1,执行时间就大幅度减小了。

2、对于随机数组,反而是增加了计算随机基准的部分,这可能是造成时间消耗上升的原因。

3、对于重复数组,不管是不是随机,划分总是n-1和1。

3、中位数为基准

说明:

在当前的一段序列中,选择第一个元素值、最后一个元素值、中间的元素值,三者中的中位数作为基准。

如序列:5,8,3,4,9,2,7,6,1。三个值为5,9,1。则选择5作为基准

核心代码:

int mid=(start+end)/2;
if(nums[mid]>nums[end])
        swap(nums[mid],nums[end]);
if(nums[start]>nums[end])
    	swap(nums[start],nums[end]);
if(nums[start]<nums[mid])
    	swap(nums[start],nums[mid]);
int baseValue = nums[start];

运行时间:

(时间单位:ms)随机数组降序数组升序数组重复数组
基本快速排序240149847015181801518230
随机基准9807906501520030
中位数为基准250110701512310

分析: 

1、对于降序数组和升序数组,取中作为基准,则能更好的将序列平分

2、对随机数组看来没有什么影响

3、对重复数组,无影响,反正值都是一样的,取不取中无所谓

4、插入+取中

说明:

1、使用中位数作为基准

2、在数列长度小于一定值时,采用插入排序

核心代码:

void quicktSort(vector<int> &nums, int start, int end)
{
    if (end <= start)
        return;
     
    int divideLoc = partition(nums, start, end);
    if(divideLoc>start+1)
    {
    		if(divideLoc-start<10)
    		{
    			for (int i = start+1; i < divideLoc; i++)
        			for (int j = i; j >= start && (nums[j] < nums[j - 1]); j--)
            			swap(nums[j], nums[j - 1]);
    		}
    		else
    			quicktSort(nums, start, divideLoc - 1);
    }
    		
    if(divideLoc<end-1)
    {
    		if(end - divideLoc <10)
    		{
    			for (int i = divideLoc+2; i <= end; i++)
        			for (int j = i; j >= divideLoc + 1 && (nums[j] < nums[j - 1]); j--)
            			swap(nums[j], nums[j - 1]);
    		}
    		else
    			quicktSort(nums, divideLoc + 1, end);
    }
}

运行时间:

(时间单位:ms)随机数组降序数组升序数组重复数组
基本快速排序240149847015181801518230
随机基准9807906501520030
中位数为基准250110701512310
取中+插入25090601520800

分析:

可以看出来,对于降序和升序数组,有提升,但是并不大,甚至说可以忽略,可能是选择使用插入排序的时机不对

5、聚集+取中

说明:

将与基准相同的数字,聚集到基准周围,下次进行划分是,这些元素就不必参加划分

例如:

待排序数列: 5,8,5,3,5,4,9,2,7,5,6,1,5。

选择5作为基准,则划分后的序列为:1,2,4,3,5,5,9,5,7,5,6,8,5,划分位置的下标为4

在进行划分的时候,与基准相同的数字,要么在基准的左侧,要么在基准的右侧

将与基准相同的元素聚集到基准周围:1,2,4,3,5,5,5,5,5,9,6,8,7

核心代码:

void gather(vector<int> &nums, int &left, int &mid, int &end)
{
	int loc=mid+1;
	for(int i=mid+1;i<=end;i++)
	{
		if(nums[i]==nums[mid])
		{
			swap(nums[i],nums[loc]);
			loc++;
		}
	}
	left=loc;
}

运行时间:

(时间单位:ms)随机数组降序数组升序数组重复数组
基本快速排序240149847015181801518230
随机基准9807906501520030
中位数为基准250110701512310
取中+插入25090601520800
取中+聚集33021015010

分析:

1、随机、降序、升序数组不包含重复元素,因此聚集步骤等于浪费时间

2、对于重复数组,则时间大大提高

6、聚集+插入+取中

运行时间:

(时间单位:ms)随机数组降序数组升序数组重复数组
基本快速排序240149847015181801518230
随机基准9807906501520030
中位数为基准250110701512310
取中+插入25090601520800
取中+聚集33021015010
取中+插入+聚集30019011010

 分析:

总体上来看,取中+插入+聚集可以达到很好的效率。

7、内置sort函数

(时间单位:ms)随机数组降序数组升序数组重复数组
取中+插入+聚集30019011010
sort340120150230

 总起来看,好像比内置的sort函数还要好,感觉有点不可能,是不是自己哪里写错了。

代码可以在下面链接进行下载:

https://download.csdn.net/download/liu_feng_zi_/12844738

有错误的地方或者其他疑问,请指出,共同探讨。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值