【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)

news2024/11/24 2:11:46

🌟🌟作者主页:ephemerals__

🌟🌟所属专栏:算法

目录​​​​​​​

前言

一、三路快排的整体思路

二、三路快排的具体实现

1.测试数据、交换函数和三数取中法

2.三路快排函数

三、程序全部代码

总结


前言

        之前我们学习了快速排序算法及其实现:

【排序算法】八大排序(下)(c语言实现)(附源码)-CSDN博客

不过它的缺陷也很明显:当数组中存在大量相同元素时,那些与基准值相同的元素的划分方法是未定义的,这将导致运行效率的下降。基于此问题,今天给大家介绍快速排序的升级版--三路快排,它能够很大程度地解决大量数据相同的情况。

一、三路快排的整体思路

        所谓三路快排,就是从快速排序的划分上,由原来的两部分变为三部分:左边是比基准值小的数据;中间是与基准值相同的数据;右边是比基准值大的数据这样划分出来,之后递归细分时,每一次生成的中间部分就不需要再进行划分了,提高了整体的运行效率

       之前我们的快速排序当中,都是默认将数组首元素作为基准值如果该值是数组的最大值或者最小值,那么划分操作相当于只是将该值进行了排序,时间复杂度就会退化为O(N^2)。所以接下来我们在选取基准值时,将会使用三数取中法将首元素、末元素与中间元素进行比较,选取一个中间值作为基准值

        划分的具体步骤如下:

1. 创建两“指针” left 和 right ,分别指向待排区间的两端;找到基准值之后与left位置交换,让其位于首元素位置

2. 创建“指针” cur,指向left的后一个位置

3. 当cur遇到比基准值的元素时,其与left位置交换,然后cur和left各向后走一步

    当cur遇到比基准值的元素时,其与right位置交换,然后right向前走一步

    当cur遇到与基准值相同元素时,cur向后走一步,访问后面的元素

4. 当cur超过right位置时,划分结束,退出循环

我们画图模拟一下它的划分过程:

注意划分完成后left和right所处的位置,便于确定下次递归的区间。

二、三路快排的具体实现

        接下来,我们开始实现三路快排。首先写好测试数据、交换两元素的函数与三数取中法:

1.测试数据、交换函数和三数取中法

#include <stdio.h>

int main()
{
	int arr[] = { 9,4,1,5,5,6,2,2,2,8,4,4,0,3,3,8,7 };//测试数据
	int sz = sizeof(arr) / sizeof(arr[0]);//计算出数组大小

	//这里排序数据

	for (int i = 0; i < sz; i++)//打印数组
	{
		printf("%d ", arr[i]);
	}
	return 0;
}
//交换两元素
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
//三数取中法,返回中间值的下标
int MidOfThree(int* arr, int a, int b, int c)
{
	if (arr[a] > arr[b])
	{
		if (arr[b] > arr[c])
		{
			return b;
		}
		else if (arr[a] > arr[c])
		{
			return c;
		}
		else
		{
			return a;
		}
	}
	else
	{
		if (arr[a] > arr[c])
		{
			return a;
		}
		else if (arr[b] > arr[c])
		{
			return c;
		}
		else
		{
			return b;
		}
	}
}

2.三路快排函数

接下来,我们尝试实现三路快排的划分以及递归:

//三路快排
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)//划分的区间达到最小,停止递归
	{
		return;
	}

	//记录左右边界与中间元素的下标
	int begin = left;
	int end = right;
	int mid = (begin + end) / 2;

	//确定基准值,并将其与首元素交换
	Swap(&arr[MidOfThree(arr, begin, mid, end)], &arr[left]);

	int key = arr[left];//记录基准值
	int cur = left + 1;

	//遍历数组,开始划分
	while (cur <= right)
	{
		if (arr[cur] < key)//比基准值小的情况
		{
			Swap(&arr[cur++], &arr[left++]);
		}
		else if (arr[cur] > key)//比基准值大的情况
		{
			Swap(&arr[cur], &arr[right--]);
		}
		else//与基准值相等的情况
		{
			cur++;
		}
	}

	//划分完成后,递归遍历左区间和右区间,中间部分不需要再参与排序
	//注意此时left和right的位置
	QuickSort(arr, begin, left - 1);
	QuickSort(arr, right + 1, end);
}

测试结果:

可以看到,数组排序成功了。

三、程序全部代码

三路快排的相关程序全部代码如下:

#include <stdio.h>

//交换两元素
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//三数取中法,返回中间值的下标
int MidOfThree(int* arr, int a, int b, int c)
{
	if (arr[a] > arr[b])
	{
		if (arr[b] > arr[c])
		{
			return b;
		}
		else if (arr[a] > arr[c])
		{
			return c;
		}
		else
		{
			return a;
		}
	}
	else
	{
		if (arr[a] > arr[c])
		{
			return a;
		}
		else if (arr[b] > arr[c])
		{
			return c;
		}
		else
		{
			return b;
		}
	}
}

//三路快排
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)//划分的区间达到最小,停止递归
	{
		return;
	}

	//记录左右边界与中间元素的下标
	int begin = left;
	int end = right;
	int mid = (begin + end) / 2;

	//确定基准值,并将其与首元素交换
	Swap(&arr[MidOfThree(arr, begin, mid, end)], &arr[left]);

	int key = arr[left];//记录基准值
	int cur = left + 1;

	//遍历数组,开始划分
	while (cur <= right)
	{
		if (arr[cur] < key)//比基准值小的情况
		{
			Swap(&arr[cur++], &arr[left++]);
		}
		else if (arr[cur] > key)//比基准值大的情况
		{
			Swap(&arr[cur], &arr[right--]);
		}
		else//与基准值相等的情况
		{
			cur++;
		}
	}

	//划分完成后,递归遍历左区间和右区间,中间部分不需要再参与排序
	//注意此时left和right的位置
	QuickSort(arr, begin, left - 1);
	QuickSort(arr, right + 1, end);
}

int main()
{
	int arr[] = { 9,4,1,5,5,6,2,2,2,8,4,4,0,3,3,8,7 };//测试数据
	int sz = sizeof(arr) / sizeof(arr[0]);//计算出数组大小

	//这里排序数据
	QuickSort(arr, 0, sz - 1);

	for (int i = 0; i < sz; i++)//打印数组
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

总结

        快速排序是一种高效且常用的排序算法,但是传统的快排并没有对与基准值相同的数据进行明确划分,造成运行效率的降低。因此出现了三路快排,它按照基准值将数组分成了三份:左边是比基准值小的数据;中间是与基准值相同的数据;右边是比基准值大的数据。这样,与基准值相同的数据就不需要再次划分,提高了整体的运行效率。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

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

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

相关文章

vue手机端 搜索框调起带搜索键盘,点击确认自动关闭

效果如下图 步骤&#xff1a; 1.html,所需配置参数都在下图 <el-form :inline"true" :mode"serchFormf" class"searchForm" action"javascript:return true;"><el-form-item label"" ><el-inputsize"…

Oracle DG备库应用延迟问题分析处理

1.情况说明 分享一个之前的案例&#xff0c;客户备库出现间歇性的日志应用延迟&#xff0c;延迟最高时刻需经过约50分钟左右才能将日志追平。 2.详细记录 2.1、问题发现 数据库的DG1备库出现间歇性的日志应用延迟&#xff0c;具体表现为备库apply lag延迟过高。影响到数据库的…

基于Atlas200DK部署yolov5(v6.0)

Atlas200_YOLOv5_DVPP_AIPP 在Atlas200DK平台上基于DVPP和AIPP实现部署YOLOv5&#xff08;v6.0&#xff09; DVPP解码JPG&#xff0c;并Resize AIPP实现颜色转换&#xff1a;YUV420sp_U8 to RGB 效果&#xff1a; 前处理推理后处理5.21ms1.09ms4.41ms 环境&#xff1a;…

【hot100篇-python刷题记录】【多数元素】

R6-技巧篇 直接哈希表计数得了 查找最大&#xff0c;可以不用一次遍历&#xff0c;直接sort排序哈希表&#xff08;按值排列&#xff09;也可以 class Solution:def majorityElement(self, nums: List[int]) -> int:nlen(nums)tn//2dictdefaultdict(int)for num in nums:d…

C语言练习题2

1.基本运算符 /*基本运算符*/ #include<stdio.h> int main() {int a 10;int b 20;printf("%d %d %d %d", a b, a - b, a * b, a / b); }加减乘除是语言当中最基本的运算符。因为除法中都是整数&#xff0c;所以除法的结果只保留整数。要想获取小数部分除法…

IntelliJ IDEA修改默认.m2和.gradle缓存路径

IntelliJ IDEA修改默认.m2和.gradle缓存路径 在idea.exe安装路径下&#xff0c;找到\bin\idea.properties文件&#xff0c;打开&#xff0c;在后面追加两行&#xff08;如果没有&#xff09;&#xff1a; gradle.user.homeD:/program/gradle新位置maven_repositoryD:/program/m…

微服务框架一

微服务 配置描述 没有配置中心 建立配置中心后 配置中心在微服务的地位 主流配置中心对比 安装Nacos open API配置管理测试 配置外部mysql连接 Nacos每层含义 命名空间

从零开始掌握Vue实例

从零开始掌握Vue实例&#xff1a;深入理解数据绑定与生命周期的核心秘诀 引言 简要介绍主题&#xff1a; 在学习Vue.js的过程中&#xff0c;Vue实例是最基础也是最关键的部分。Vue实例是Vue应用的核心&#xff0c;它是数据、DOM元素和Vue组件之间的桥梁。掌握Vue实例的使用对于…

基于 ASP.NET的教材管理信息系统的设计与实现(最新定制开发,阿龙原创设计)✅

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

前端开发学习Docker记录02容器操作

docker images先查看有哪些镜像 运行nginx docker run nginx使用docker ps 可以看到有哪些镜像在运行 docker stats查看运行状态 命令总结

Linux学习(16)-http协议(应用层)与web服务器

本节学习内容 1.HTTP协议基础概念 2.代码实现通过http协议与web端建立连接 一、HTTP协议&#xff08;超文本传输协议&#xff09; 1.HTTP协议为应用层协议其底层的传输层为TCP协议 2.HTTP协议的固定端口号为80&#xff08;HTTPS端口为443&#xff09;&#xff0c;小于1024…

计算机毕业设计PySpark+Django考研分数线预测 考研院校推荐系统 考研推荐系统 考研爬虫 考研大数据 Hadoop 大数据毕设 机器学习 深度学习

考研推荐系统— 项目概述 考研&#xff08;研究生入学考试&#xff09;是许多大学毕业生追求深造的一种途径。为了帮助考生更好地选择适合自己的研究生专业和院校&#xff0c;开发一个考研推荐系统可以为考生提供个性化的建议。该项目旨在通过数据分析和可视化技术&#xff0…

【OpenCV】不变矩

不变矩 图像矩零阶矩空间矩质心 中心矩归一化中心矩Hu矩 Hu矩不变性的代码体现附录 参考 图像矩 在计算图像矩是需要将图像转化为单通道的灰度图&#xff0c;或者进行二值化处理&#xff0c;即目标对象设为1&#xff0c;背景设为0。 图像的矩是将图像像素灰度值进行加权平均&…

62.一个机器人位于一个 m x n 网格的左上角 。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。实现一个算法计算路径的数量

62. Unique Paths 题目 一个机器人位于一个 m x n 网格的左上角(在下图中标记为 ‘Start’)。 机器人每次只能向下或向右移动。机器人试图到达网格的右下角(在下图中标记为 ‘Finish’)。 问有多少条不同的路径可以到达终点? 上面是一个 7 x 3 的网格。问有多少条不同…

Spring Boot简介与体系知识导图

Spring Boot是Spring开源组织下的一个子项目&#xff0c;是一个基于Spring框架的快速开发脚手架&#xff0c;它极大地简化了Spring应用的初始化和搭建过程&#xff0c;为开发者提供了快速、简单的方式来开发、部署和管理Spring应用。以下是关于Spring Boot的详细介绍&#xff1…

BaseCTF Week2

Week2 Web [Week2] 一起吃豆豆 [Week2] 你听不到我的声音 无回显RCE 参考博客&#xff1a; BMZCTF&#xff1a;shell_exec_ctf exec(command:“ls >> 1.txt”)-CSDN博客RCE绕过之无回显_ctf无回显rce-CSDN博客 [Week2] ez_ser PHP反序列化。 参考博客&#xff1a;[h…

js发送邮件:如何在Node.js实现邮件发送?

js发送邮件如何与服务器配置&#xff1f;如何用nodejs发送邮件&#xff1f; 无论是用于用户注册确认、密码重置还是简单的通知&#xff0c;js发送邮件都是不可或缺的一环。AokSend将详细介绍如何在Node.js环境中实现这一功能&#xff0c;确保你能够轻松掌握js发送邮件的技巧。…

Android APK打包脚本

build.gradle版本 同目录创建config.gradle文件写入需要的信息入 config.gradle文件内容 ext { /*** 自定义APP运行环境* dev: 开发* test: 测试* pro: 生产*/ env "pro" /*** 动态参数配置&#xff0c;根据自己需要添加参数* APP_ID: 包名* VERSION_CODE: 版本号…

【Ubuntu20.04】配置深度学习环境

参考 Ubuntu20.04配置深度学习环境&#xff08;全网最细最全&#xff09; NVIDIA显卡驱动安装安装CUDA 通过终端nvidia-smi查看自己能安装的最高CUDA版本&#xff0c;在官方网址下载需要的版本。安装cuDNN 在官方网址选择适配于自己安装的CUDA版本的cuDNN安装Anaconda &#x…

Logrus IT的质量评估门户如何提高游戏本地化质量:案例研究

确保高质量的游戏本地化对于全球成功至关重要&#xff0c;特别是对于玩家在设置和游戏许可证上投入巨资的AAA和AA游戏。这些球员往往要求苛刻&#xff0c;一丝不苟。尽管客户公司通过严格控制配音人才的选拔、录音质量等&#xff0c;尽最大努力在所有语言中保持高标准&#xff…