如何优化快速排序?

news2025/1/18 15:55:32

欢迎来到 Claffic 的博客 💞💞💞

前言: 

还记得上次的快速排序吗?还记得 key 是怎么取的吗?当时我直接把数组的起始元素作为了 key 值,其实这样做是有弊端的,试想:一个降序的数列,要排列成升序的序列,最大的数字作为 key 值,那岂不是效率较低?所以,这就意味着仍有优化的空间,那么这期就来优化下快速排序。

注:

这篇博客属于数据结构内容,建议学习完C语言的小伙伴食用~


目录

🍕Part1:取随机数法

1.思想

2.实现

🍔Part2:三数取中法

1.思想

2.实现

🍟Part3:小区间调整

1.思路

2.实现

🌭Part4:三种方法对比

1.测试性能的试金石 

2.开始测试


Part1:取随机数法

1.思想

这个思想就是字面意思啦 ~ 就是取一个数组中极大值与极小值之间的一个随机数

为什么这样做会优化一点时间呢?

我们回忆一下快速排序,它的思想就是找一个 key 值,一趟下来,数组 左部分 的数字都 小于 key 右部分 的数字都 大于 key

我们进行调整,就是在 key 的选取上下功夫,

试想:你选择的 key 值是数组中最大的,一趟下来,只有 key 值的位置变了,那岂不是挫到家了?

我们要尽量避免这种情况发生,就可以随机一些,数字的量越大,取到最大值的概率越小,近似认为避免了这种情况。

2.实现

实现很简单,加一个生成随机数的函数,由于取的是 keyi,即 key 的下标,所以随机数模上数组的长度,再加上 left 即可(一趟过后开始递归, 左部分下标不是从0开始 ):

//快排(前后指针法)(取随机数优化)
void QuickSort3(int* a, int left, int right)
{
	if (left >= right)
		return;

	// 取随机数
	//int randi = left + (rand() % (right - left));
	//Swap(&a[randi], &a[left]); // 交换randi与left对应的数字

	int keyi = left;
	int key = a[left];
	int prev = left;
	int cur = left + 1;

	while (cur <= right)
	{
		if (a[cur] < key && ++prev != cur)
			Swap(&a[cur], &a[prev]);
		cur++;
	}
	Swap(&a[keyi], &a[prev]);//与下标为keyi的交换
	keyi = prev;

	//递归
	QuickSort3(a, left, keyi - 1);
	QuickSort3(a, keyi + 1, right);

}

Part2:三数取中法

1.思想

在看随机数排列的时候,我相信你已经开始思考这个问题了:

 与其说是避免取到不合适的值,不如说是直接取最合适的值

是的,三数取中法就是取最合适的值,那么什么样的值是最合适的呢?

中值最合适;

如何找到中值呢?

最简单的就是取三个值:最左值,最右值,中间值挨个比较;

下面看看是如何实现的:

2.实现

//三数取中
int GetMidi(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
			return mid;
		else if (a[left] > a[right])
			return left;
		else
			return right;
	}
	else // a[left] > a[mid]
	{
		if (a[left] < a[right])
			return left;
		else if (a[mid] > a[right])
			return mid;
		else
			return right;
	}
}

解释:

其实就是分两种大情况:

在这两种大情况下取中。

接入排序中,就是这样的: 

	int midi = GetMidi(a, left, right);
	if (midi != left)
		Swap(&a[midi], &a[left]);

 

Part3:小区间调整

1.思路

有这种调整方法是因为当数据数量较小时,利用快速排序未免有杀鸡焉用牛刀的感觉,

想想,五个数字排有序,需要调用七次快排函数,是不是大题小作?

所以在这种数据量小的情况下,不妨利用其他的排序,

那么选择哪个排序呢?

选择最灵活的, 稳定的,那就是插入排序。

2.实现

可以在数字长度小于10的情况下进行插入排序,大于10就进行快速排序:

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	int keyi = PartSort3(a, left, right);
	// 小区间调整
	if ((right - left + 1) > 10)
	{
		QuickSort(a, left, keyi - 1);
		QuickSort(a, keyi + 1, right);
	}

	InsertSort(a + left, right - left + 1);// 插入排序不再赘叙
}

Part4:三种方法对比

1.测试性能的试金石 

生成N个随机数,测试每种排序的性能,输出的是排序所用的时间(单位毫秒) 

// 测试排序的性能对比
void TestOP()
{
	srand(time(0));
	const int N = 1000000;// 数据的数量
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);

	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
	}

	int begin1 = clock();
	//InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	//SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin5 = clock();
	QuickSort2(a5, 0, N - 1);
	int end5 = clock();

	int begin6 = clock();
	//MergeSort(a6, N);
	int end6 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("QuickSort:%d\n", end5 - begin5);
	printf("MergeSort:%d\n", end6 - begin6);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
}

2.开始测试

这里只测试快速排序:

优化方法名称第一次测试第二次测试第三次测试第四次测试第五次测试
取随机数151150150151154
三数取中135135135140133
小区间调整174193169178178
取随机数+小区间调整190186217190191
三数取中+小区间调整182178171171174

注意:只是简单的样本采集(甚至不算样本),不具有统计学意义,仅供参考

似乎单独三数取中法是最好的... ...

我做完之后对这个数据挺诧异的: 

为什么三数取中法与小区间调整共用之后更慢了

有研究过的大佬可以教教我🤣

代码已上传至 我的gitee 

拿走不谢~


总结: 

(总结)

码文不易 

如果你觉得这篇文章还不错并且对你有帮助,不妨支持一波哦  💗💗💗

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/428023.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP Business Technology Platform (BTP)的架构理解

查资料看到的&#xff0c;转一下&#xff0c;附上链接&#xff1a; SAP Business Technology Platform (BTP)的架构理解 长期以来&#xff0c;我在与客户和伙伴的沟通交流中发现大家依然对SAP业务技术平台 – SAP Business Technology Platform (以下简称BTP)纯有各种疑惑&…

webgl-图形非矩阵旋转

知识拓展 由&#xff08;x1,y1&#xff09;旋转β角度到&#xff08;x2,y2&#xff09; 根据圆极坐标方程 x1 r*cosα y1 r*sinα 可得 x2 r*cos(α β) r*cosα*cosβ - r*sinα*sinβ,因为x1 r*cosα&#xff0c;y1 r*sinα&#xff0c;所以x2 x1*cosβ -y1*sinβ…

如何进行移动设备资产管理

随着越来越多的移动设备进入和访问组织的企业资源&#xff0c;管理员必须监视和控制对企业数据的访问。与传统工作站不同&#xff0c;传统工作站位于企业的物理工作区内&#xff0c;移动设备从多个位置使用&#xff0c;从而使移动资产管理过程更加复杂。 什么是移动资产管理 …

java基础集合面试题

什么是集合 集合就是一个放数据的容器&#xff0c;准确的说是放数据对象引用的容器 集合类存放的都是对象的引用&#xff0c;而不是对象的本身 集合类型主要有3种&#xff1a;set(集&#xff09;、list(列表&#xff09;和map(映射)。 集合的特点 集合的特点主要有如下两点&…

LMKD分享

背景 Android是一个多任务系统&#xff0c;可以同时运行多个程序&#xff0c;一般来说&#xff0c;启动运行一个程序是有一定的时间开销的&#xff0c;因此为了加快运行速度&#xff0c;当你退出一个程序时&#xff0c;Android并不会立即杀掉它&#xff0c;这样下次再运行该程…

【MySQL优化】快速入门慢SQL优化

MySQL B树结构&#xff08;二叉排序树&#xff09; 索引 SQL优化&#xff0c;主要就是在优化索引 索引:相当于书的目录 索引:index是帮助MYSQL高效获取数据的数据结构。索引是数据结构&#xff08;树:B树(默认)、Hash树…) 索引的弊端: 1.索引本身很大&#xff0c;可以存…

Spring Cloud Config配置服务及那些你不知道的坑

目录 1、为什么选择Spring Cloud Config 1.1 集中式管理 1.2 动态修改配置 2、Spring Cloud Config 简介 3、服务端配置 3.1 添加依赖 3.2 开启服务注册 3.3 添加YML配置 3.4 创建远程分支及Profile配置文件 3.5 启动并测试服务 4、客户端配置 4.1 添加依赖 4.2 开…

2.3-6循环链表

原理的单链表和循环单链表的区别&#xff1a; 初始化循环单链表时&#xff0c;使头节点next指针指向头节点。 判断循环单链表是否为空。 对比&#xff1a; 单链表&#xff1a;if(L->next NULL) 双链表&#xff1a;if(L->nextL) 判断循环单链表的结点p是否为表尾结点…

汇聚音视频新能量 探索行业新蓝海

视频行业卷成红海&#xff0c;如何突破瓶颈&#xff0c;去探索行业的新蓝海&#xff0c;本次LiveVideoStackCon 2022北京站邀请到快手高级副总裁、研发线负责人于冰&#xff0c;以《汇聚音视频新能量&#xff0c;探索行业新蓝海》为题&#xff0c;从视频行业趋势和痛点出发&…

类ChatGPT模型ChatGLM-b6本地部署实践

国外ChatGPT火爆持续&#xff0c;前一段时间百度发布“文心一言”还没有全面放开测试&#xff0c;这不阿里“通义千问”又悄然而至&#xff0c;国内大模型AI产品渐渐浮出水面。早在2022年8月份时候清华大学的对话语言模型ChatGLM-6B就发布并开源&#xff0c;本文简要介绍ChatGL…

flex弹性布局详细介绍

这里提供一个可以边学习边玩的flex学习网站&#xff1a;弹性盒青蛙 目录一、Flex布局是什么&#xff1f;二、属性1. justify-content 属性2. align-items属性3. flex-direction属性4. order属性5. align-self属性6. flex-wrap 属性7. flex-flow属性8. align-content属性三、综合…

测试新人必看,软件测试测试流程

不同类型的软件产品测试的方式和重点不一样&#xff0c;测试流程也会不一样。同样类型的软件产品&#xff0c;不同的公司所制定的测试流程也会不一样。虽然不同软件的详细测试步骤不同&#xff0c;但它们所遵循的最基本的测试流程是一样的。 1分析测试需求 2制定测试计划 3设…

Python opencv 实现图像平移及旋转

Python opencv 实现图像平移及旋转 仿射变换是一种仅在二维平面中发生的几何变形&#xff0c;变换之后的图像仍然可以保持直线的 “平直性” 和 “平行性”&#xff0c;也就是说原来的直线变换之后还是直线&#xff0c;平行线变换之后还是平行线。图像平移和图像旋转是常见的放…

IMX6ULL-IRQ中断之添加中断向量表

一. 中断向量表 中断向量表是存放中断向量的表。中断服务程序的入口地址或存放中断服务程序的首地址成为中断向量&#xff0c;因此中断向量表是一系列中断服务程序入口地址组成的表。当有中断事件发生时&#xff0c;处理器通过中断向量表进入相关的中断服务程序处理事件。 二.…

自动化测试(二):QTP验证点

1 、程序自带验证点 自带验证点&#xff1a;图形界面insert -> checkpoint Standard Checkpoint 标准验证&#xff1a;用于检查测试对象的属性 Text Checkpoint 文本验证&#xff1a;用于检查文本字符串是否在应用程序中的适当位置出现 Text Area Checkpoint文本区域验…

饮用水中的六价铬去除工艺详解

铬是人体必需的微量元素&#xff0c;天然水不含铬&#xff0c;海水中铬的平均浓度为0.05μg/L&#xff0c;饮用水中铬含量更低。 铬在水中主要以三价和六价形式存在&#xff0c;三价的铬是对人体有益的元素&#xff0c;而六价铬是有毒的。由于其毒性之高&#xff0c;已被国家列…

Smith预估器

Smith预估器主要针对存在大滞后的系统,作用延迟和反馈延迟环节的控制,Smith预估器的另一篇文章,请参看下面的博客文章: 博途1200/1500PLC Smith预估器(补偿器)算法实现(FB)_RXXW_Dor的博客-CSDN博客在写这篇文章之前写过一篇"大林控制算法",大家可以参看下面这…

免费的包噪音网站分享

免费的包噪音网站分享 现代生活中&#xff0c;噪音扰人&#xff0c;影响健康和情绪。白噪音可以为人们提供放松心情、提高睡眠质量和专注力的帮助。 现在有很多免费的白噪音网站可以任意使用和分享&#xff0c;包括海浪声、雨声、蝉鸣声等等&#xff0c;非常适合在办公室、家里…

windows 下安装 ffmpeg

介绍一下我的环境及开发软件版本 windows phpstudy php7.3 进入安装步骤 1.下载windows系统的FFMpeg 下载链接&#xff1a; http://ffmpeg.org/download.html ps: 这里有各种版本了 &#xff08;未使用这个版本&#xff09;git地址&#xff1a;https://github.com/BtbN/…

数据结构初阶 - 汇总

-0- 数据结构前言 什么是数据结构 什么是算法 数据结构和算法的重要性-1- 时间复杂度和空间复杂度 &#x1f449;数据结构 -1- 时间复杂度和空间复杂度 | C 算法效率 时间复杂度大O的渐进表示法eg 空间复杂度 常见复杂度对比OJ 消失的数组 轮转数组-2- 顺序表 与 链表 &am…