快速排序

快速排序是分治策略的一个应用。

算法思想就是基准元素的划分,这点可以参考书上的分析。

主要给出快速排序算法的分析:

快速排序算法的时间主要耗费在划分操作上,并与划分是否平衡密切相关。对于长度为n的待排序序列,一次划分算法Partition需要堆整个待排序序列扫描一遍,其所需要的计算时间显然为O(n)。

下面从三种情况来讨论一下快速排序算法的时间复杂度。

(1)最坏时间复杂度

最坏情况是每次划分选取的基准元素都是在当前待排序序列中的最小(或最大)元素,划分的结果所基准元素左边的子序列为空(或右边的子序列为空),而划分所得的另一个非空的子序列中元素个数仅仅比划分前的排序序列中元素个数少1个。

在这样的情况下,快速排序算法必须做n-1次划分,那么算法的运行时间T(n)的递归形式为:

                         O(1)                                         n = 1

T(n) =

                          T(n-1) + O(n)                   n > 1

因此,快速排序算法的最坏时间复杂度为O(n*(n+1)/2)=O(n*n)

(2)最好时间复杂度

在最好情况下,每次划分所去的基准元素都是当前待排序序列的“中间值”,划分的结果所基准元素的左右两个子序列的长度大致相同,此时,算法的运行时间T(n)的递归形式为:

                         O(1)                                         n = 1

T(n) =

                         2 T(n/2) + O(n)                   n > 1

解出后得到最好时间复杂度为O(nlogn).

(3)平均时间复杂度

在平均情况下,采用归纳法可求得T(n)的数量级也为O(nlogn).

尽管快速排序的最坏时间复杂度为n*n,但是就平均性能来说,它是基于元素比较的内部排序算法中速度最快的,因此得名:Quick_Sort


快速排序的空间复杂度

因为快速排序采用递归执行,所以需要一个栈来存放每一层递归调用的必要信息,其最大容量与递归调用的深度一致。最好情况下,若每次划分都比较均匀,则递归树的高度为O(logn),故递归需要栈空间为O(logn)。最坏情况下,递归树的高度为O(n),所需要的栈空间为O(n)。平均情况下,所需要的栈空间为O(logn)。


 快速排序也有一个有趣的副产品:快速选择给出的一些数中第k小的数。一种简单的方法是使用上述任一种O(nlogn)的算法对这些数进行排序并返回排序后数组的第k个元素。快速选择(Quick Select)算法可以在平均O(n)的时间完成这一操作。它的最坏情况同快速排序一样,也是O(n^2)。在每一次分割后,我们都可以知道比关键字小的数有多少个,从而确定了关键字在所有数中是第几小的。我们假设关键字是第m小。如果k=m,那么我们就找到了答案——第k小元素即该关键字。否则,我们递归地计算左边或者右边:当k<m时,我们递归地寻找左边的元素中第k小的;当k>m时,我们递归地寻找右边的元素中第k-m小的数。由于我们不考虑所有的数的顺序,只需要递归其中的一边,因此复杂度大大降低。

给出测试代码:

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;

const int N = 10;
int arr[N];

/******************快速排序稳定版本*************************************/
void QuickSort(int low, int high)
{
	if(low >= high)
		return ;
	int i, j, pivot;
	i = low, j = high, pivot = arr[(high + low) >> 1]; //每次取中间元素作为基准
	swap(arr[low], arr[(high + low) >> 1]);
	while(i < j)
	{
		while(i < j && arr[j] >= pivot) --j;
		arr[i] = arr[j];
		while(i < j && arr[i] <= pivot) ++i;
		arr[j] = arr[i];
	}
	arr[i] = pivot;
	QuickSort(low, i - 1); //递归左边
	QuickSort(j + 1, high); //递归右边
}
/*************************************************************************/

/******************快速排序一般版本(可求第K大元素)*****************/
int Partition(int low, int high)
{
	int i, j, pivot;
	i = low, j = high, pivot = arr[low];
	while(i < j)
	{
		while(i < j && arr[j] >= pivot) --j;
		if(i < j) swap(arr[i++], arr[j]);
		while(i < j && arr[i] <= pivot) ++i;
		if(i < j) swap(arr[i], arr[j--]);
	}
	return j; //返回基准元素位置
}

void Quick_Sort(int low, int high)
{
	int pivotpos;
	if(low < high)
	{
		pivotpos = Partition(low, high);
		Quick_Sort(low, pivotpos - 1); //基准左递归排序
		Quick_Sort(pivotpos + 1, high); //基准右递归排序
	}
}

int select(int low, int high, int k)
{
	int pivotpos, num;
	if(low == high)
		return arr[high];
	pivotpos = Partition(low, high);
	num = pivotpos - low + 1; //左边元素个数
	if(k == num)
		return arr[pivotpos];
	else if(k < num)
		return select(low, pivotpos - 1, k);
	else
		return select(pivotpos + 1, high, k - num);
}
/*************************************************************************/

int main()
{
	int k;
	cout<<"输入10个数字:"<<endl;
	for(int i = 0; i < 10; ++i)
		scanf("%d", &arr[i]);
	QuickSort(0, 9);
	cout<<"快速排序后:"<<endl;
	for(int i = 0; i < 10; ++i)
		cout<<arr[i]<<" ";
	cout<<endl;
	cout<<"输入第K大元素:"<<endl;
	cin>>k;
	cout<<select(0, 9, k)<<endl;
	return 0;
}


 

你可能感兴趣的:(快速排序)