快速排序

快速排序的效率主要取决于你是怎么来实现这个算法的,如果中间有一步的实现没有做好,快速排序可能会变得非常慢。

快速排序的步骤:

  1. 选主元(pivot):在待排序数组中选取一个数作为主元。
  2. 子集划分:围绕所选取的主元划分数组,主元左边的数均小于主元,主元右边的数均大于主元。(这样划分后主元的位置已经确定)
  3. 递归:将主元左边的元素和主元右边的子集元素分别递归的执行上面两步,直到数组完全有序。

这里主要的步骤之一就是选主元,主元怎么选关乎整个算法的效率。经分析可知当每次所选的主元都是数组元素的中间值,即比它大的元素和比它小的元素个数近似相等时,每一次都可以将数组平分,算法效率最高。

由图可得选取数组的第一个数或最后一个数为主元都是不可取的,因为如果数组本来就有序,会造成每次主元所划分的数组有一边为空。

用生成随机数的方法取主元也不可取,因为rand()函数会耗费大量时间。

其实选取主元的方法有很多种,下面我们介绍一种最常见的方法:取头、中、尾中位数作主元(还有五位数取中位数和七位数取中位数的取法)。

取头中尾的中尾数即取数组的头部、中间、尾部这三个数中的中位数作为主元,例如a[0]=8,a[(n-1)/2]=12,a[n-1]=3;则选取8作为主元。

子集划分:设待划分数组的左界为L,右界为R,将主元选好后,把头、中、尾中最小的放在a[L],最大的放在a[R],中位数放在a[(R+L)/2];将主元和a[R-1]互换位置(即给主元一个临时位置,以便于接下来子集的划分),设置两个指针i和j,由于a[R]和a[L]上的元素已经确定是大于主元和小于主元的所以不用参与划分,令i=L+1,j=R-2,(因为R-1上放置的是主元),while(a[i]<pivot)i++;while(a[j]>pivot)j--;当i指向大于pivot的元素,j指向小于pivot的元素时,交换a[i],a[j],重复上述步骤,直到i>j时循环停止;将a[i]和a[R-1]互换即可。

注意:若a[i]或a[j]与pivot相等最好也要停下来互换位置,想象一下如果一个数组所有的元素都相等,如果a[i]与pivot相等时不停下来的话,会导致主元的位置放在两边,两边子列分组极不平衡,所以虽然停下来交换会增加交换次数,但是可以使子列近似平分。

由于快速排序是递归实现的,对于小规模的数据(例如N<100),快速排序可能还没有插入排序快,所以我们可以设置一个阈值,当递归的数据规模充分小,则停止递归,直接调用简单排序。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h> 
#define size 10000
#define cutoff 100
//由于快速排序是递归实现的,对于小规模的数据(例如N<100),快速排序可能还没有插入
//排序快,所以我们可以设置一个阈值,当递归的数据规模充分小,则停止递归,直接调用简单排序。

void get_pivot(int a[],int start,int end);//将主元调整到end-1的位置 
void quike_sort(int a[],int n);//快速排序 
void sort(int a[],int start,int end);

void Insert_sort(int a[],int n);//插入排序 

void print(int a[],int n)
{
	int i;
	for(i=0;i<n;i++)
		printf("%d ",a[i]);
}

int main()
{
	srand((unsigned)time(NULL));
	int i;
	int a[size];
	for(i=0;i<size;i++)
		a[i]=rand()%100000;
	printf("排序前:");
	print(a,size);
	int time1=clock();
	quike_sort(a,size);
	int time2=clock();
	printf("\n排序后:");
	print(a,size);
	printf("\n算法运行时间:%d\n",time2-time1);
	return 0;
} 

void get_pivot(int a[],int start,int end)
{
	int middle=(start+end)/2;
	int e;
	if(a[middle]<a[start])
	{
		e=a[middle];
		a[middle]=a[start];
		a[start]=e;
	}
	if(a[end]<a[start])
	{
		e=a[start];
		a[start]=a[end];
		a[end]=e;
	}
	//此时a[start]一定是最小的 
	if(a[middle]>a[end])
	{
		e=a[middle];
		a[middle]=a[end];
		a[end]=e;
	}
	e=a[middle];
	a[middle]=a[end-1];
	a[end-1]=e;		
}

void quike_sort(int a[],int n)
{
	sort(a,0,n-1);                                                                                           
}

void sort(int a[],int start,int end)
{
	if(end-start>cutoff)
	{
		get_pivot(a,start,end);
		int i=start;
		int j=end-1;
		int e;
		while(1)
		{
			while(a[++i]<a[end-1]);
			while(a[--j]>a[end-1]);
			if(i<=j)
			{
				e=a[i];
				a[i]=a[j];
				a[j]=e;
			}
			else break;
		}
		e=a[i];
		a[i]=a[end-1];
		a[end-1]=e;
		sort(a,start,i-1);
		sort(a,i+1,end);
	}
	else
		Insert_sort(&a[start],end-start+1);
}

void Insert_sort(int a[],int n)
{
	int i;
	for(i=1;i<n;i++)
	{
		int temp=a[i];
		for(;i>0&&a[i-1]>temp;i--)
			a[i]=a[i-1];
		a[i]=temp;
	}
} 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力攻坚操作系统

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值