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

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

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

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

相较优化前,如果数组是全由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);
}
相关推荐
KaMeidebaby16 小时前
卡梅德生物技术快报|PD1 单克隆抗体定制配套 N 糖全谱质控开发
前端·人工智能·算法·数据挖掘·数据分析
8Qi817 小时前
LeetCode 235. 二叉搜索树的最近公共祖先(LCA)
算法·leetcode·二叉树·递归·二叉搜索树·lca·迭代
bIo7lyA8v17 小时前
算法稳定性分析中的随机扰动建模的技术8
算法
sugar__salt17 小时前
从栈队列数据结构到JS原型面向对象全解
前端·javascript·数据结构
科研online18 小时前
基于多源数据和XGBoost-SHAP分析中国大陆绿地碳汇空间变异影响因素的非线性相关性与尺度差异
算法·学习方法
Cthy_hy18 小时前
拓扑排序超详解:原理 + Kahn 贪心算法
python·算法·贪心算法
三品吉他手会点灯18 小时前
C语言学习笔记 - 43.运算符与表达式 - 运算符1 - 运算符的分类和简单介绍
c语言·笔记·学习·算法
VkN2X2X4b18 小时前
算法复杂度的实验验证与误差分析的技术8
算法
其利天下技术19 小时前
风扇灯无刷电机自适应算法实战指南
算法·cocos2d·无刷电机自适应算法·bldc驱动自适应算法·其利无刷电机驱动算法
8Qi819 小时前
LeetCode 494:目标和(Target Sum)—— 题解 ✅
算法·leetcode·职场和发展·动态规划·01背包