快速排序的三路划分和自省排序

三路划分主要是为了解决数组中重复的数数量太多导致的多次递归

按照基准值把数组分成三部分

比基准值小的,比基准值大的,等于基准值的,

相较优化前,如果数组是全由2组成的数组,会递归很多次,但三路划分优化后就很快

三路划分

代码

复制代码
void kuai_pai_max(int* a, int start, int end)
{
	
	if (start >= end)
		return;

	if (end - start + 1 < 11)
	{
		cha_ru(a + start, end - start + 1);
		return;
	}
	
	int cur = start+1;
	int left = start;
	int right = end;

	int* q = san_zhong(&a[(left + right) / 2], &a[left], &a[right]);

	jiao_huan(&a[left], q);

	int zhong = a[left];

	while (cur <= right)
	{
		if (a[cur] > zhong)
		{
			jiao_huan(&a[cur],&a[right--]);
		}
		else if (a[cur] == zhong)
		{
			cur++;
		}
		else
		{
			jiao_huan(&a[cur++], &a[left++]);
		}
	}


	kuai_pai_max(a, start, left - 1);
	kuai_pai_max(a, right + 1, end);

	return;
}

一个左指针 left 初始化指向最左边,

cur 表示当前查看的位置初始指向最左边加一

right 右指针初始化指向最右面

定义完是一小步三数取中获得基准值,用整型变量zhong记一下之后开始判断cur位置的值

三种可能,大于,小于,等于基准值

若大于 跟right指针指向的位置交换值,然后right减一

若小于 跟left指针指向的位置交换值,然后left和cur都加一

若等于 cur加一,看下一个值

最后cur大于right时结束循环

左边到left-1就是小于基准值的

right+1到右边就是大于基准值的

left到right就是等于基准值的

原理

cur从左往右走,left左边放入了所有小于基准值的数

right右边放入了所有大于基准值的数

如果不大于基准值,不可能在right右边

如果不小于基准值,不可能在left左边

可以看出left刚开始指向的就是基准值

如果跟cur交换,cur还在left前面加一的位置,换完都加一,left指向的数还是基准值

啥时候cur不在left下一个位置,只有cur指向的值等于基准值时才会cur自己往后走

所以可以得出left指向的是基准值,并且左开右闭区间 [ left , cur ) 里一直都是基准值

cur>right循环才会结束,循环结束的时候cur在right下一位

所以结束时 left , right 区间就都是基准值了。

cur会与right相遇一次,若相遇时right若这个位置不是基准值,

那么right会减一,

刚好在cur前一位。

自省排序

目的:

在上一个优化后加上一个条件,递归层数超过 2 * log n 就别再递归下去了,改用堆排序

正常情况下深度不会超过 2 * log n ,只有出现某些极限的组合才会超过,

那么就不要再往下递归,一条路到底,圈出区间,改为堆排序接着排序

处理特殊情况,保证效率

代码

复制代码
void zi_sheng(int* a, int start, int end,int n,int m)
{

	if (start >= end)
		return;

	if (end - start + 1 < 11)
	{
		cha_ru(a + start, end - start + 1);
		return;
	}

	if (n > m)
	{
		HeapSort(&a[start], end - start + 1);
		return;
	}


	int cur = start + 1;
	int left = start;
	int right = end;

	int* q = san_zhong(&a[(start + end) / 2], &a[start], &a[end]);

	jiao_huan(&a[left], q);

	int zhong = a[left];

	while (cur <= right)
	{
		if (a[cur] > zhong)
		{
			jiao_huan(&a[cur], &a[right--]);
		}
		else if (a[cur] == zhong)
		{
			cur++;
		}
		else
		{
			jiao_huan(&a[cur++], &a[left++]);
		}
	}


	zi_sheng(a, start, left - 1,n + 1, m);
	zi_sheng(a, right + 1, end, n + 1, m);

	return;
}

跟上一个代码没多大区别

就多传俩值 n m 和 多写个 if

每次递归传的就是n+1和一直不变的m

不变的 m 表示最大深度,超过了就换堆排序,n 表示当前深度

判断 n 是否大于 m 了

封装 (优化)

形参有 数组地址,区间左边,右边,当前递归深度,最大深度2 * log n

整整5个形参

封装就是让使用者不用传这么多实参

如果知道数组地址,有多少个元素。(size)

就传这俩的话

第一次区间左边就是 0 右边是size - 1 当前递归深度 0 最大深度拿size算一下

可以看出,只需要传入地址和元素个数size就能算出其他

代码

复制代码
//算 log2(n) 向下取整
int log2_floor(int size)
{
	int cnt = 0;
	while (size > 1)
	{
		size /= 2;
		cnt++;
	}
	return cnt;
}

// 封装调用,对外只需要传数组和长度
void Introsort(int* a, int size)
{
	if (size <= 1) return;
	int max_depth = 2 * log2_floor(size); // 自省最大深度
	zi_sheng(a, 0, size - 1, 0, max_depth);
}
相关推荐
土司大王5 小时前
LeetCode hot100——对称二叉树
算法·leetcode·职场和发展
sali-tec6 小时前
C# 基于OpenCv的视觉工作流-章106-差值追踪
图像处理·人工智能·opencv·算法·计算机视觉
Navigator_Z6 小时前
LeetCode //C - 1208. Get Equal Substrings Within Budget
c语言·算法·leetcode
手写码匠6 小时前
Dify 多 Agent 工具权限与安全沙箱实战:让智能体“有能力,但不越权“
人工智能·深度学习·算法·aigc
黎阳之光6 小时前
打破堆场感知黑盒:黎阳之光视频孪生,构建港口码头网格化透明管控新体系
大数据·人工智能·算法·安全·数字孪生
大模型码小白7 小时前
AI 对话流性能调优:万级消息的虚拟滚动落地
java·大数据·前端·javascript·人工智能·算法·机器学习
sel_97 小时前
【多轮对话论文导读(三)】多轮对话与Agent论文阅读笔记:用户模拟、轨迹生成与长期记忆
论文阅读·人工智能·笔记·深度学习·算法·机器学习
Asize8 小时前
438. 找到字符串中所有字母异位词
算法
Asize8 小时前
283. 移动零
算法
船厂电气自动化ai大模型10 小时前
AI大模型与数学·第57课 傅里叶全套工具链综合实战:串联级数/连续变换/DFT/FFT,图像、音频、扩散模型完整例题
数据结构·人工智能·深度学习·算法·机器学习