详解八大排序(六)------(三路划分,自省排序,归并排序外排序)

1. 快排之三路划分

1. 1 三路划分的诞生由来

通过详解八大排序(三)------(快速排序)的介绍。我们可以知道,决定快排性能的关键是key的寻找:若是每次寻找的key都能将数组二分之一,那么快排的性能最佳;若是每次寻找的key都是将数组分成 1 和 N-1 个数,那么快排的时间复杂度就是O(N^2)。
通过 前后指针寻找随机key 我们可以解决绝大部分数组的找key需求。但是也会有一小部分的数组需求没有实现。
例如:

在这里插入图片描述

在面对含有大量重复数据的数组时,hoare版本 和 lomuto版本 的性能都有一定程度的退化。而三路划分是针对大量重复数据的找key方法。

1. 2 三路划分的具体思路

三路划分的核心思路可以理解为 hoare版本 和 lomuto版本 的结合:将数组划分成三个区域,比key小的区域A,等于key的区域B,大于key的区域C。
通过左右指针的方式,将小于key的数放进区域A,大于key的数放进区域C。
再对区域A和C进行划分。直到全部划分完成。

具体的实现思路:

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

  2. left指向区间最左边,right指向区间最后边,cur指向left+1位置。
    在这里插入图片描述

  3. cur遇到比key小的值后跟left位置交换,换到左边,left++,cur++。

例如上面的数组,由于1 < 6,所以1换到6的位置上。

在这里插入图片描述

一直重复,直到 left 交换完。得到:
在这里插入图片描述

  1. cur遇到比key大的值后跟right位置交换,换到右边,right-- 。

接着以上述数组举例。左边数组交换完成,接着交换右边数组:

在这里插入图片描述

重复交换,得到:

在这里插入图片描述

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

  2. 直到cur > right结束

最终得到:

在这里插入图片描述

这样就将数组分成了三个区域。

1. 3 代码实现

//三路划分
void ThreeWaySort(int* arr, int left, int right)
{
   
   
	if (left >= right)
	{
   
   
		return;
	}

	//取一个随机值当key
	int randi = left + (rand() % (right - left + 1));
	Swap(&arr[left], &arr[randi]);

	int key = arr[left];
	int cur = left + 1;

	int begin = left,end = right;

	while (cur <= right)
	{
   
   
		if (arr[cur] < key)
		{
   
   
			Swap(&arr[left++], &arr[cur++]);
		}
		else if (arr[cur] > key)
		{
   
   
			Swap(&arr[cur], &arr[right--]);
		}
		else 
		{
   
   
			cur++;
		}
	}

	ThreeWaySort(arr, begin, left - 1);
	ThreeWaySort(arr, right + 1, end);
}

2. 快排之自省排序

2. 1 自省排序的目的

自省排序的诞生和三路划分相似,都是原有的排序在面对新的问题时,出现效率不足的情况。
三路划分面对数组中含有大量重复数据的情况,具有显著的性能提升。但是在其他情况下就很一般,同时三路划分相比一般的快排具有一定的损耗。
这个时候就有自省排序的出现。

2. 2 自省排序的思路

自省排序的本质很简单。就是通过一个深度检测器—可以理解为当前排序的时间复杂度检测器。判断当前快排的深度有没有超过logn。超过了就说明当前排序的效率是有问题的。然后接下来过程就不再使用快排,而是使用堆排序进行排序。

2. 3 自省排序的实现代码

void AdJustDown(int* arr,int sz,int parent)
{
   
   
	int child = parent * 2 + 1;
	while (child < sz)
	{
   
   
		if (child + 1 < sz && arr[child + 1] > arr[child])
		{
   
   
			++child;
		}

		if (arr[child] > arr[parent])
		{
   
   
			Swap(&arr[child], &arr[parent]);
			parent = child
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值