快速排序的深入优化——三路划分,内省排序(C语言)

1. 快排性能的关键点分析

决定快排性能的关键点是每次单趟排序后,key对数组的分割,如果每次选key基本⼆分居中,那么快 排的递归树就是颗均匀的满⼆叉树,性能最佳。但是实践中虽然不可能每次都是⼆分居中,但是性能 也还是可控的。但是如果出现每次选到最⼩值/最⼤值,划分为0个和N-1的⼦问题时,时间复杂度为 O(N^2),数组序列有序时就会出现这样的问题,我们前⾯已经⽤三数取中或者随机选key解决了这个问题,也就是说我们解决了绝⼤多数的问题,但是现在还是有⼀些场景没解决(数组中有⼤量重复数据 时),例如下面:

多个key值相等,或者全是相同的数。

cpp 复制代码
int a[] = { 6,1,7,6,6,6,4,9 };
int a[] = { 3,2,3,3,3,3,2,3 };
int a[] = { 3,3,3,3,3,3,3,3 };

2.三路划分算法

这里使用三路划分可以提升当数组中,有大量相同的值时,排序的时间效率,三路划分的核⼼思想有点类似hoare的左右指针和lomuto的前后指针的结合(这里的hoare的左右指针和lomuto的前后指针在小编前面"各种排序方法"文章中有所讲解)。核⼼思想是把数组中的数据分为三段**⽐key⼩的值;跟key相等的值;⽐key⼤的值**,所以叫做三路划分算法。

实现的思想:

  1. key默认取left位置的值。

  2. left指向区间最左边,right指向区间最后边,cur指向left+1位置。

  3. cur遇到⽐key⼩的值后跟left位置交换,换到左边,left++,cur++

  4. cur遇到⽐key⼤的值后跟right位置交换,换到右边,right--。

  5. cur遇到跟key相等的值后,cur++。

  6. 直到cur>right结束

排序成这样以后,再递归left之前的数组,和right之后的数组就可以了。

注意:这种排序方法,主要是当数组中有大量相同的数据时,时间效率较高,但如果数组中没有大量相同的数据,这种算法的时间效率甚至还不如,lomuto和hoare版的快速排序。

三路划分针对有⼤量重复数据时,效率很好,其他场景就⼀般,但是三路划分思路还是很有价 值的,有些快排思想变形体,要⽤划分去选数,他能保证跟key相等的数都排到中间去,三路划分的价值就体现出来了。

cpp 复制代码
void _QuicksortPro(int* arr, int left, int right)//三路划分:有大量相同数据
{
	if (left >= right)
		return;
	int begin = left;
	int end = right;
	int randi = left + (rand()%(right - left + 1));
	Swap(&arr[left], &arr[randi]);
	int key = arr[left];
	int cur = left + 1;
	while (cur <= right)
	{
		if (arr[cur] < key)
		{
			Swap(&arr[left++], &arr[cur++]);
		}
		else if (arr[cur > key])
		{
			Swap(&arr[cur], &arr[right--]);
		}
		else
		{
			cur++;
		}
	}
	_QuicksortPro(arr, begin, left - 1);
	_QuicksortPro(arr, right + 1, end);
}

void Quicksort(int* arr, int left, int right)
{
	srand(time(0));
	_QuicksortPro(arr, left, right);
}

3.introsort的快排排序(内省排序)

introsort是由DavidMusser在1997年设计的排序算法,C++sgi STL sort中就是⽤的introspective sort(内省排序)思想实现的。内省排序可以认为不受数据分布的影响,⽆论什么原因划分不均匀, 导致递归深度太深,他就是转换堆排了,堆排不受数据分布影响。

首先确定递归深度达到多少时,深度一般为logn的两倍(数据个数取二的对数:2*logn),就停止这种排序转换成堆排序(堆排序,在小编之前"堆的应用"文章中已有详细讲解);

其次当数组长度小于十六时,就直接使用插入排序,简单递归次数;

代码如下:

cpp 复制代码
//直接插入排序
void InsertSort(int* arr, int size)
{
	for (int i = 0; i < size-1; i++)
	{
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
			if (tmp < arr[end])
			{
				arr[end + 1] = arr[end];
			}
			else {
				break;
			}
			end--;
		}
		arr[end + 1] = tmp;
	}
}

//堆排序
void AdjustDwon(int* a, int n, int root)
{
	int parent = root;
	int child = 2 * parent + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] < a[child + 1])
		{
			child += 1;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapSort(int* a, int n)
{
	int parent = (n - 1 - 1) / 2;
	while (parent >= 0)
	{
		AdjustDwon(a, n, parent);
		parent--;
	}
	int size = n - 1;
	while (size > 0)
	{
		Swap(&a[0], &a[size]);
		AdjustDwon(a, size, 0);
		size--;
	}
}

void IntroSort(int* arr, int left, int right, int depth, int defaultDepth)
{
	if (left >= right)
	{
		return;
	}
	if (right - left + 1 < 16)
	{
		InsertSort(arr + left, right - left + 1);
			return;
	}
	if (depth > defaultDepth)
	{
		HeapSort(arr+ left, right - left + 1);
		return;
	}
	depth++;

	int begin = left;
	int end = right;

	int randi = left + (rand() % (right - left + 1));
	Swap(&arr[randi], &arr[left]);

	int keyi = left;
	int prev = left, cur = left + 1;
	while (cur <=right)
	{
		if (arr[cur] < arr[keyi] && ++prev != cur)
		{
			Swap(&arr[cur], &arr[prev]);
		}
		cur++;
	}
	Swap(&arr[keyi], &arr[prev]);
	keyi=prev;

	IntroSort(arr, begin, keyi - 1, depth, defaultDepth);
	IntroSort(arr, keyi+1, end, depth, defaultDepth);
}

void QuickSort(int *arr,int left,int right)
{
    int depth = 0;
    int logn = 0;
    int size=right-left+1; 
    for (int i = 1; i < size; i*=2)
    {
    	logn++;
    }
    IntroSort(arr, left, right, depth,logn*2);
}
相关推荐
清梦202019 分钟前
经典问题---跳跃游戏II(贪心算法)
算法·游戏·贪心算法
Dream_Snowar42 分钟前
速通Python 第四节——函数
开发语言·python·算法
1nullptr1 小时前
三次翻转实现数组元素的旋转
数据结构
Altair澳汰尔1 小时前
数据分析和AI丨知识图谱,AI革命中数据集成和模型构建的关键推动者
人工智能·算法·机器学习·数据分析·知识图谱
TT哇1 小时前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
嵌入式科普1 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
A懿轩A1 小时前
C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·栈和队列
Python机器学习AI1 小时前
分类模型的预测概率解读:3D概率分布可视化的直观呈现
算法·机器学习·分类
吕小明么2 小时前
OpenAI o3 “震撼” 发布后回归技术本身的审视与进一步思考
人工智能·深度学习·算法·aigc·agi
1 9 J3 小时前
数据结构 C/C++(实验五:图)
c语言·数据结构·c++·学习·算法