122.【C语言】数据结构之快速排序(Hoare排序的优化)

目录

1.解决方法(即优化方法)

方法1.随机选key

运行结果

方法2:三数取中

1.含义

2.做法

3.代码

[1.若arrleft < arrmid_i,则arrright可能的位置也有三处](#1.若arr[left] < arr[mid_i],则arr[right]可能的位置也有三处)

[2.若arrleft > arrmid_i,则arrright可能的位置也有三处](#2.若arr[left] > arr[mid_i],则arr[right]可能的位置也有三处)

2.证明当key_i=left时,right先走,使left和right相遇的位置比arrkey_i


针对121.【C语言】数据结构之快速排序(未优化的Hoare排序存在的问题)以及时间复杂度的分析文章分析出的问题,本文讲一部分问题的解决方法

1.解决方法(即优化方法)

由分析排有序的时间复杂度可以看出:选取key的值很重要,如何提高排有序或接近有序数组的效率呢? 有两种方法:1.随机选key 2.三数取中(这两种方法能解决121.【C语言】数据结构之快速排序(未优化的Hoare排序存在的问题)以及时间复杂度的分析文章测试代码1和2的问题,但不能解决测试代码3产生的问题)

方法1.随机选key

key_i不一定初始为0,因此不在左侧,可以随机选key来提高排序的效率,但注意随机值的下标应该在区间left,right范围内

***则key_i=left+x(0≦x≦right-left)***

得出随机值的下标rand_i,但下方的循环是按照key_i=left设计的,因此需要交换arrleft和arrrand_i位置来使arrkey_i获得新的值

随机选key_i不一定每次选到最小或最大的,因此效率高

cpp 复制代码
void QuickSort_Hoare(int* arr, int left, int right)
{
	if (left >= right)
		return;

	//单趟快速排序
	int begin = left;
	int end = right;
	srand((unsigned int)time(0));
	int rand_i = left+rand() % (right - left);
	Swap(&arr[left], &arr[rand_i]);//随机找一个值与左边交换

	int key_i = left;//最终还是左边做key_i
	while (left < right)
	{
        //......
    }
    //......
}

121.【C语言】数据结构之快速排序(未优化的Hoare排序存在的问题)以及时间复杂度的分析文章的测试代码1和测试代码2去测试含随机选key的Hoare快速排序

运行结果

正常运行

修改测试代码1的N为3000,看看优化前后的Hoare快速排序消耗的时间

优化前

优化后

方法2:三数取中

1.含义

三个数值取中间数值对应的下标

2.做法

设mid_i为left和right的中间值,在arrleft,arrmiddle,arrright中取既不是最大也不是最小的数,显然需要两两比较

3.代码

先讨论arrleft和arrmid_i的大小关系

1.若arrleft < arrmid_i,则arrright可能的位置也有三处
cpp 复制代码
	if (arr[left] < arr[mid_i])
	{
		if (arr[right] > arr[mid_i])
		{
			int key_i = mid_i;
		}
		else if (arr[right] > arr[left])
		{
			int key_i = right;
		}
		else
		{
			int key_i = left;
		}
	}
    else
    {
        //......
    }
2.若arrleft > arrmid_i,则arrright可能的位置也有三处
cpp 复制代码
	if (arr[left] < arr[mid_i])
	{
        //......
	}
	else
	{
		if (arr[right] > arr[left])
		{
			int key_i = left;
		}
		else if (arr[right] > arr[mid_i])
		{
			int key_i = right;
		}
		else
		{
			int key_i = mid_i;
		}
	}

写到函数里

cpp 复制代码
int GetMiddleNum(int left,int right,int*arr)
{
	int mid_i = (left + right) / 2;
	if (arr[left] < arr[mid_i])
	{
		if (arr[right] > arr[mid_i])
		{
			return mid_i;
		}
		else if (arr[right] > arr[left])
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else
	{
		if (arr[right] > arr[left])
		{
			return left;
		}
		else if (arr[right] > arr[mid_i])
		{
			return right;
		}
		else
		{
			return mid_i;
		}
	}
}

优化后的Hoare快速排序写为

cpp 复制代码
void QuickSort_Hoare(int* arr, int left, int right)
{
	if (left >= right)
		return;

	//单趟快速排序
	int begin = left;
	int end = right;
	
	int ret = GetMiddleNum(left, right, arr);
	if (left != ret)//避免left自己和自己交换
	{
		Swap(&arr[left], &arr[ret]);
	}
	int key_i = left;
	while (left < right)
    {
        //......
    }
    //......
}

修改测试代码1的N为3000,看看优化前后的Hoare快速排序消耗的时间

优化前

优化后

注意:三数取中在数组有序或接近有序时速度比随机数快,可以用测试性能代码试试看

总结:随机选key和三数取中可以优化排有序数组的时间复杂度,使其小于N^2,加了优化几乎不会有最坏情况,因此时间复杂度为N*logN

2.证明当key_i=left时,right先走,使left和right相遇的位置比arrkey_i

左边做key,右边先走,相遇位置的值比arrkey小,需要分类讨论,设排升序,设指针left从左向右找大,指针right从右向左走找小

情况1:right找到小,但left一直没有找到大,导致right和left相遇的位置的值比arrkey_i要小

情况2:right找小没找到,一直左移,和left相遇,相遇的位置的值可能和arrkey_i相等,也可能比arrkey_i要小

同理可以推出:右边做key,左边先走,相遇位置的值比arrkey

相关推荐
地平线开发者3 小时前
profiler debug 工具用法与高一致性策略
算法·自动驾驶
编程大师哥3 小时前
匿名函数 lambda + 高阶函数
java·python·算法
isyangli_blog3 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008113 小时前
FastAPI APIRouter
开发语言·python
Benszen3 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆3 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木3 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
我叫袁小陌3 小时前
算法解题思路指南
算法
地平线开发者3 小时前
Conv+BN+Add+ReLU 融合机制简介
算法·自动驾驶
也曾看到过繁星4 小时前
数据结构---顺序表
数据结构