排序总结系列五:快速排序

1.快速排序的思想
经过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录比关键字大的,一部分记录比关键字小的,在可分别对这两部分记录继续进行排序,以达到整体有序的目的。
<pre name="code" class="cpp">void Qsort(int *ar, int left, int right);    //函数声明
int partition(int *arr, int left, int right);//函数声明
/
void quick_sort(int *arr, int len)            //排错
{
	assert(arr != NULL);
	Qsort(arr, 0, len - 1);
}
void Qsort(int *arr, int left, int right)      //左右来筛
{
	int mid = 0;
	if (left < right)
	{
		mid = partition(arr, left, right);//一次划分
		Qsort(arr, left,  mid - 1);
		Qsort(arr, mid + 1, right);
	}
}
int partition(int *arr, int left, int right)
{
	int key = arr[right];//注意和下面左筛右筛对应
	while (left < right)
	{
		while (left < right && arr[left] <= key)    //左筛
		{
			left++;
		}
		if (left < right) swap(arr, left, right);       //交换
		while (left < right && arr[right] >= key)//右筛
		{
			right--;
		}
		if (left < right) swap(arr, left, right);       //交换
	}
	return left;
}
///
void swap(int *arr, int left, int right)
{
	int tmp = arr[left];
	arr[left] = arr[right];
	arr[right] = tmp;
}
partition函数要做的事情,就是先选取其中的一个关键字,然后想办法将它放到一个位置,使得它左边的值都比它小,右边的值都比它大,我们称这样的关键字为枢纽。pivot
2.空间时间
时间: 最优情况O(nlogn) 最坏情况0(n^2)
空间: 最好情况O(log2n) 最坏情况O(n) 平均情况 空间O(logn);
稳定: 不稳定
3.快速排序的优化:
3.1优化选取枢轴: 随机取中法。
三数取中法。(取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数。
int pivotkey;
int m=low + (high-low)/2;
if(ar[low] > ar[high)    swap(ar,low,high);
if(ar[m] > ar[high])     swap(ar,high,m);
if(ar[m] > ar[low])       swap(ar,m,low);
pivotkey = ar[low];
3.2优化不必要的交换
3.3优化小数组时的排序方案
3.4优化递归操作。
/
/**使用递归快速排序**/
template<typename Comparable>
void quicksort1(vector<Comparable> &vec, int low, int high)
{
	if (vec.empty() || low < 0) return;
	if (low < high)
	{
		int mid = partition(vec, low, high);
		quicksort1(vec, low, mid - 1);
		quicksort1(vec, mid + 1, high);
	}
}
/**使用栈的非递归快速排序**/
<pre name="code" class="cpp">/**使用栈的非递归快速排序**/
template<typename Comparable>
void quicksort2(vector<Comparable> &vec, int low, int high)
{
	stack<int> st;
	if (low < high)
	{
		int mid = partition(vec, low, high);
		if (low < mid-1)
		{
			st.push(low);
			st.push(mid-1);
		}
		if (mid+1 < high)
		{
			st.push(mid+1);
			st.push(high);
		}
		//其实就是用栈保存每一个待排序子串的首尾元素下标,下一次while
		//循环时取出这个范围,对这段子序列进行partition操作
		while (!st.empty())
		{
			int high = st.top(); //范围的右边
			st.pop();
			int low = st.top();  //范围的左边
			st.pop();
			mid = partition(vec, low, high);
			if (low < mid - 1)
			{
				st.push(low);
				st.push(mid-1);
			}
			if (mid + 1< high)
			{
				st.push(mid+1);
				st.push(low);
			}
		}
	}
}
//一次划分
template <typename Comparable>
int partition(vector<Comparable> &vec, int low, int high)
{
	Comparable pivot = vec[low];  //任选元素作为轴,这里选首元素
	while (low < high)
        {
		while (low < high && vec[high] >= pivot)  high--;
		if (low < high) vec[low] = vec[high];

		while (low < high && vec[low] <= pivot)   low++;
		if (low < high) vec[high] = vec[low];
		
	 }
	vec[low] = pivot;                    //此时low==high
	return low;
}
 
可以看到非递归的算法比递归实现还要慢。下面解释为什么会这样。
递归算法使用的栈由程序自动产生,栈中包含:函数调用时的参数和函数中的局部变量。如果局部变量很多或者函数内部又调用了其他函数,则栈会很大。每次递归调用都要操作很大的栈,效率自然会下降。
而对于非递归算法,每次循环使用自己预先创建的栈,因此不管程序复杂度如何,都不会影响程序效率。
但对于上面的快速排序,由于局部变量只有一个mid,栈很小,所以效率并不比非递归实现的低。
//
对长度为n 的线性表作快速排序,在最坏情况下,比较次数为n(n-1)/2.
快速排序的最坏情况就是数组有序,选的基数要和每一个数进行比较,(n-1)+(n-2)+...+1=n(n-1)/2次。











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值