排序算法之快速排序

在讲解 快速排序 之前,我们提前引入了两个问题:

快速排序基石之荷兰国旗问题

快速排序基石之荷兰国旗问题II

如果你还未研读上面的两个题目,建议先研习一下,因为我们这章要讲解的快速排序原理就是基于上面两个问题的解决方式来做的

快速排序可以分为 3 个版本:

1.0 版本:荷兰国旗问题

固定选择最末元素为 num 值,保证 num 前的数组内容分为两部分

完成后 num 与 >num 区域的第一个元素交换,这样 num 确定好了位置

剩余就是递归新的两部分

代码部分:

int partition1(int *arr, int l, int r)
{	
	int less = l - 1;
	int more = r;
	
	while(l < more)
	{
		if(arr[l] <= arr[r])
		{
			swap(arr, ++less, l++);
		}
		else
		{
			l++;
		}
	}
	swap(arr, less+1, r);
	
	return less+1;
}

void doquickSort1(int *arr, int l, int r)
{
	if(l < r)
	{
		int pIdx = partition1(arr, l, r);

		doquickSort1(arr, l, pIdx-1);
		doquickSort1(arr, pIdx+1, r);
	}
}

2.0 版本:荷兰国旗问题II

固定选择最末元素为 num 值,保证 num 前的数组内容分为三部分

完成后 num 与 >num 区域的第一个元素交换,这样 num 确定好了位置一定在 == num 区域

剩余就是递归新的两部分

相比较 1.0 因为一次搞定了一批 == num 的数字,所以效率有提升

 

代码部分:

int* partition2(int *arr, int l, int r)
{	
	int less = l - 1;
	int more = r;
	
	while(l < more)
	{
		if(arr[l] < arr[r])
		{
			swap(arr, ++less, l++);
		}
		else if(arr[l] > arr[r])
		{
			swap(arr, --more, l);
		}
		else
		{
			l++;
		}
	}
	swap(arr, more, r);
	
	int *pIdx = (int *)malloc(sizeof(int) * 2);
	pIdx[0] = less+1;
	pIdx[1] = more;
	
	return pIdx;
}

void doquickSort2(int *arr, int l, int r)
{
	if(l < r)
	{
		int* pIdx = partition2(arr, l, r);    // 数组指代 == num 的区间坐标
		int newl = pIdx[0];
		int newr = pIdx[1];

		free(pIdx);
		doquickSort2(arr, l, newl-1);
		doquickSort2(arr, newr+1, r);
	}
}

3.0 版本:荷兰国旗问题II

面对1.0 或者 2.0 版本,面对最差情况,都是 O(N^2)

比如 1,2,3,4,5,6,7,8,9 (num 选定的很偏)

当然最好的情况就是选定的 num 在中间位置左右,这样左右的区间规模几乎相等

按照 master 公式(忘记的参考这里:时间复杂度之 Master 公式

T(N) = 2 * T(N / 2) + O(N)   (左右规模都是 N /2,其余消耗在做partition过程)

这样计算出来的时间复杂度 = O(N* logN)

num 逐渐打偏就会退化成 O(N^2)的算法

采用范围内的随机选择一个数,当做num放到末尾

因为随机选的数,好情况与坏情况在概率上来讲都是一样的,每一个num造成的T(N)值都是等概率事件,都是 1 / N 的概率

把所有的数进行概率累加 + 数学上的长期期望得到时间复杂度 = O(N* logN)

数学的推理不在此讨论范围,记住就行

代码:

相对于2.0 版本仅有一个改动部分,就是不选用固定元素作为num

int randIdx = l + rand() % (r - l + 1);
swap(arr, randIdx, r); 
int* pIdx = partition2(arr, l, r);    // 依旧用 3 个区域

更多数据结构算法详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值