排序:快速排序

1.概述

快速排序是最慢的冒泡排序的升级,它们都属于交换排序类。也是通过不断比较的移动交换来实现交换的,与冒泡排序相比,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面,从而减少了总的比较次数和移动交换次数。

基本思想是:

通过一趟排序将待排序记录分割成成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。与归并排序一样,它也是一种分治的递归算法。

2.实现

基本实现:

void swap(int A[],int i,int j)
{
	int tmp=A[j];
	A[j]=A[i];
	A[i]=tmp;
}
//找出枢纽元的位置
int Partition(int A[],int left,int right)
{
	int privotkey;
	privotkey=A[left];
	while(left<right)
	{
		while(left<right&&A[right]>=privotkey)
			right--;
		swap(A,left,right);//从右开始找一个比枢纽元小的元素交换到底端
		while(left<right&&A[left]<=privotkey)
			left++;
		swap(A,left,right);//从左开始找一个比枢纽元大的元素交换到高端
	}
	return left;

}
void QSort(int A[],int left,int right)//待排序序列的最小下标值left和最大下标值right
{
	int priovt;
	if(left<right)
	{
		priovt=Partition(A,left,right);

		QSort(A,left,priovt-1);
		QSort(A,priovt+1,right);
	}
}
void QuickSort(int A[],int N)
{
	QSort(A,0,N-1);
}
Partition函数的作用,从当前序列当中选取一个关键字,然后把它放到一个位置,使得它左边的值都比它小,右边的值比它大,这样的关键字称为枢纽元。

快速排序优化:

(1)优化选取枢纽元

通常没有经过考虑的选择就是将第一个元素用作枢纽元。如果输入是随机的那么还可以接受,但是如果输入是预排序或者反序的,那么这样的枢纽元选择就会产生一个恶劣的分割。

随机选取枢纽元:除了极端情况,一般来说是比较安全的,但是随机数的生成一般是比较昂贵的。

三数中值分割法:一组N个数的中值是第[N/2]个最大的数,一般的做法是,使用左端,右端和中心位置的三个元素的中值作为枢纽元,使用这个方法可以消除预排序输入的坏情况。

//找出枢纽元的位置
int Partition(int A[],int left,int right)
{
	int privotkey;

	int center=left+(right-left)/2;
	if(A[left]>A[right])
		swap(A,left,right);
	if(A[center]>A[right])
		swap(A,center,right);
	if(A[left]<A[center])
			swap(A,center,left);
	privotkey=A[left];
	while(left<right)
	{
		while(left<right&&A[right]>=privotkey)
			right--;
		swap(A,left,right);//从右开始找一个比枢纽元小的元素交换到底端
		while(left<right&&A[left]<=privotkey)
			left++;
		swap(A,left,right);//从左开始找一个比枢纽元大的元素交换到高端
	}
	return left;

}

(2)优化不必要的交换

//找出枢纽元的位置
int Partition(int A[],int left,int right)
{
	int privotkey;

	int center=left+(right-left)/2;
	if(A[left]>A[right])
		swap(A,left,right);
	if(A[center]>A[right])
		swap(A,center,right);
	if(A[left]<A[center])
			swap(A,center,left);
	privotkey=A[left];
	int tmp=privotkey;
	while(left<right)
	{
		while(left<right&&A[right]>=privotkey)
			right--;
		//swap(A,left,right);//从右开始找一个比枢纽元小的元素交换到底端
		A[left]=A[right];//采用替换而不是交换来进行操作
		while(left<right&&A[left]<=privotkey)
			left++;
		//swap(A,left,right);//从左开始找一个比枢纽元大的元素交换到高端
		A[right]=A[left];
	}
	A[left]=tmp;
	return left;

}

(3)优化小数组时的排序方案

如果数组非常小,快速排序不如直接插入排序更好,直接插入排序是简单排序中性能最好的。原因是快速排序用到了递归操作,在大量数据排序时,这点性能影响对于它的整体算法优势而言是可以忽略的。

void QSort(int A[],int left,int right)
{
	int priovt;
	if(right-left>MAX)
	{
		priovt=Partition(A,left,right);

		QSort(A,left,priovt-1);
		QSort(A,priovt+1,right);
	}
	else
		insertaion_sort(A,right-left+1);

}
MAX的值不是一定的。
(4)尾递归优化
之前的QSort在其尾部有两次递归操作,栈的大小是有限的,每次递归操作都会耗费一定的栈空间,函数的参数越多,每次耗费的空间也就越多。如果可以减少递归,将会大大提高性能。

void QSort(int A[],int left,int right)
{
	int priovt;
	if(right-left>MAX)
	{
		while(left<right)
		{
			priovt=Partition(A,left,right);
			QSort(A,left,priovt-1);
			left=priovt+1;
		}

	}
	else
		insertaion_sort(A,right-left+1);

}
3.复杂度分析

time=O(nlgn),由于其关键字的比较和交换是跳跃的,因此,快速排序是一种不稳定的排序方法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值