排序算法——快速排序

排序流程

快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1) 首先设定一个基准点,通过该基准点的值将数组分成左右两部分。
(2)将大于或等于基准点值的元素集中到数组右边,小于基准点值的元素集中到数组的左边。此时,左边部分中各元素都小于或等于基准点值,而右边部分中各元素都大于或等于基准点值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个基准点,将该部分数据分成左右两部分,右侧的数组数据也可以做类似的处理。
(4)重复上述过程,这里面包含了递归的思想。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左右两个部分各数据排序完成后,整个数组的排序也就完成了。

排序步骤

我们其实可以发现快排的本质就是给基准点找位置,每次基准点找到合适的位置后,位于基准点左侧的元素其值都小于等于基准点值,右侧的元素其值都大于等于基准点值。然后利用递归的思想将左侧的数据和右侧的数据进行排序,两边的数据都排好之后,算法结束。
按照上面提供的思路,我们快排可以通过两个功能性函数实现,Get_pivot和Quick_sort,其中Get_pivot负责利用基准点(通常是第一个数据)将数组排序分为左右两部分,返回基准点坐标。Quick_sort负责递归部分,首先对所有数据进行排序,然后根据Get_pivot返回的坐标,对左侧的数据进行排序,然后对右侧的数据进行排序。
一趟快速排序的过程是:
(1)设置两个指针i, j,排序开始的时候:i指向范围内第一个元素,j指向范围内最后一个元素(若数据通过数组存储,则下标可看作指针)。
(2)以第一个元素作为基准点,赋值给key。(临时变量存储基准点值)
(3)从j开始向前搜索,即由后开始向前搜索:若当前j指向的元素的值大于或等于key,则–j;找到第一个小于key的值,交换*j 和 *i;
(4)从i开始向后搜索,即由前开始向后搜索:若当前i指向的元素的值小于或等于key,则++i;找到第一个大于key的值,交换*i 和 *j;
(5)若i != j, 则重复第3、4步,直至i == j;

排序演示

假设一开始序列{xi}是:5,3,7,6,4,1,0,2,9,10,8。
此时,ref=5,i=0,j=10,从后往前找,第一个比5小的数是x7=2,因此序列为:2,3,7,6,4,1,0,5,9,10,8。
此时i=0,j=7,从前往后找,第一个比5大的数是x2=7,因此序列为:2,3,5,6,4,1,0,7,9,10,8。
此时,i=2,j=7,从第7位往前找,第一个比5小的数是x6=0,因此:2,3,0,6,4,1,5,7,9,10,8。
此时,i=2,j=6,从第2位往后找,第一个比5大的数是x3=6,因此:2,3,0,5,4,1,6,7,9,10,8。
此时,i=3,j=6,从第6位往前找,第一个比5小的数是x5=1,因此:2,3,0,1,4,5,6,7,9,10,8。
此时,i=3,j=5,从第3位往后找,不存在比5大的数,这时,i=j=5,ref成为一个基准点,它之前的数都比它小,之后的数都比它大,对于前后两部分数,可以采用同样的方法来排序。

程序演示(C++)

typedef vector<int>::iterator Iter;

Iter Get_pivot(vector<int>& v, Iter low, Iter high)
{
	int temp = *low;	//选择第一个数作为pivot
	while (low < high)
	{
		while (low < high && *high >= temp)
			--high;
		*low = *high;		//此时high指向小于temp的值

		while (low < high && *low <= temp)
			++low;
		*high = *low;		//此时low指向大于temp的值	
	}
	//退出循环时,low和high指向同一个地址:pivot的新地址
	*low = temp;
	return low;
}

void Quick_sort(vector<int>& v, Iter low, Iter high)
{
	Iter pivot = Get_pivot(v, low, high);
	if (pivot - low >= 2)	//检测是否需要排序,若元素个数大于等于2则需要排序
		Quick_sort(v, low, pivot - 1);
	if (high - pivot >= 2)
		Quick_sort(v, pivot + 1, high);
}

算法复杂度(性能分析)

Get_pivot单次遍历需要从两头交替搜索,直到low和high重合,因此其时间复杂度是O(n),而整个快速排序算法的复杂度其实与遍历的趟数有关。
理想的情况是,每次遍历所选择的基准点恰好将当前序列几乎等分,经过log2n趟遍历,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n).
最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次遍历所得的子表中一个为空表,另一子表的长度为n - 1。这样,长度为n的数据表的快速排序需要经过n趟遍历,使得整个排序算法时间复杂度为O(n2)。
可以证明,快速排序的平均时间复杂度是O(nlog2n)。因此,该排序方法被认为是目前最好的一种内部排序方法。
从空间性能上看,尽管快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值