算法学习:快速排序

在这里插入图片描述

🔥 个人主页:空白诗

在这里插入图片描述

文章目录

    • 🚀 引言
    • 📌 快速排序算法核心思想
      • 1. 选择基准值(Pivot)
      • 2. 分区操作(Partitioning)
      • 3. 递归排序子序列
    • 📌 JavaScript 实现
      • 1. 快速排序主函数
      • 2. 分区函数
      • 3. 示例代码
    • 📌 优化建议
      • 1. 三数取中法
      • 2. 小数组时切换排序算法
      • 3. 尾递归优化
      • 4. 并行化处理
    • 📌 时间复杂度分析
    • 📌 总结

🚀 引言

快速排序(Quick Sort)是一种高效的排序算法,由计算机科学界的传奇人物托尼·霍尔(Tony Hoare)于1960年巧妙地提出。这一算法的核心智慧在于运用了经典的分治法策略——犹如古代兵法中的“分而治之”,将一个错综复杂的大列表分割成两个相对简单的子列表,随后对这两个子列表施以同样的策略,直到每个子列表都只剩下单一元素或为空,此时整个序列自然归于有序。此过程宛如构建一座秩序井然的金字塔,自下而上,层层推进。


📌 快速排序算法核心思想

在这里插入图片描述

1. 选择基准值(Pivot)

这是算法流程的起点,从数列中精心挑选出一个元素,赋予它一个特殊角色——“基准”(pivot)。基准的选择可以很灵活,但理想情况下应倾向于选择一个能将数据集大致均匀分割的值,以促进算法效率。

2. 分区操作(Partitioning)

分区操作是快速排序的精髓所在。其目标是在遍历数列一次的过程中,通过交换元素位置,使得所有小于基准值的元素都位于基准之前,而所有大于基准值的元素都排列在其后相等的元素可以放置在任一侧,完成这一操作后,基准值恰好位于其最终排序后的位置,即序列的中间。这一巧妙划分不仅为后续递归奠定了基础,也直接体现了快速排序分而治之的哲学。

3. 递归排序子序列

基于分区结果,数列被明确划分为两个独立的部分:左侧全部小于基准值,右侧则大于基准值。接下来,算法会对这两个子序列递归地应用同样的排序逻辑。通过不断地将问题规模减半,直到每个子序列只剩下一个或零个元素(这时自然视为已排序),整个数列便会在这一系列递归调用中逐步构建出全局的有序状态。这一策略确保了快速排序高效利用了分治策略的优势,尤其是在平均情况下展现出极高的效率。


📌 JavaScript 实现

1. 快速排序主函数

function quickSort(arr, left = 0, right = arr.length - 1) {// 如果左指针小于右指针,说明还有未排序的区间if (left < right) {// 调用分区函数,返回pivot的索引,完成一次分区const pivotIndex = partition(arr, left, right);// 对pivot左边的子数组进行快速排序quickSort(arr, left, pivotIndex - 1);// 对pivot右边的子数组进行快速排序quickSort(arr, pivotIndex + 1, right);}// 返回排序后的数组,实际上由于是原地排序,此处return并非必要return arr;
}

2. 分区函数

function partition(arr, left, right) {// 选择最右侧元素作为基准值pivotconst pivot = arr[right];let i = left; // i为小于pivot的元素的边界,初始时指向最左侧// 遍历除了pivot外的所有元素for (let j = left; j < right; j++) {// 如果当前元素小于或等于pivotif (arr[j] <= pivot) {// 交换arr[i]和arr[j],并将i右移一位,保持i左侧的元素都小于等于pivot[arr[i], arr[j]] = [arr[j], arr[i]]; // ES6解构赋值进行交换i++; }}// 最后将pivot(arr[right])与arr[i]交换,使得pivot位于正确的位置上[arr[i], arr[right]] = [arr[right], arr[i]];// 返回pivot的最终位置索引return i;
}

3. 示例代码

// 未排序数组示例
const unsortedArray = [3, 6, 8, 10, 1, 2, 1];// 打印原始数组
console.log("Unsorted:", unsortedArray);// 调用快速排序函数,得到排序后的数组
const sortedArray = quickSort(unsortedArray);// 打印排序后的数组
console.log("Sorted:", sortedArray);

这段代码实现了快速排序算法,通过quickSort函数递归地将数组分为更小的子数组,并通过partition函数完成每部分的排序,最终达到整个数组有序的目的。代码中使用了ES6的解构赋值来简化元素交换的操作,使得代码更加简洁易读。


📌 优化建议

1. 三数取中法

  • 核心思想:通过更智能地选择基准值(pivot)来优化快速排序的性能。
  • 操作步骤
    1. 比较数组首部、中部、尾部的元素。
    2. 选取这三个数中的中位数作为分区操作的基准值。
    3. 这样做能有效平衡划分,即使在数据部分有序的情况下也能保持较好的性能,减少最坏情况的发生概率。

2. 小数组时切换排序算法

  • 适用条件:当待排序序列的元素数量较少时(例如少于10或15个)。
  • 策略详情:快速排序在小数组上的优势不明显,此时切换到插入排序等简单排序算法更为高效。因为插入排序在小数据集上具有较低的常数因子和无需递归的优点,能够快速完成排序,与快速排序形成互补。

3. 尾递归优化

  • 概念阐述:确保递归调用是函数的最后一个操作,便于某些支持该特性的编译器进行优化。
  • 实施方法:设计递归逻辑时,直接在递归调用的返回语句中返回计算结果,避免在递归返回后还需执行其他操作。
  • 注意事项:虽然JavaScript等语言不一定能自动优化尾递归,遵循此原则编写代码依然有助于提高可读性和未来跨平台移植的兼容性。

4. 并行化处理

  • 目标:利用多核CPU资源加速排序过程。
  • 实施步骤
    1. 将大数组分割成多个小块。
    2. 各个CPU核心独立并行地对分块数据执行快速排序。
    3. 最后合并各块已排序的结果。
  • 优势:在拥有多个处理器核心的系统上,此策略能显著缩短排序时间,尤其适合处理海量数据。

通过上述一系列优化措施,快速排序算法不仅在理论上保持了较高的时间效率,在实际应用中也变得更加灵活和健壮,能够有效应对各种规模数据集的排序挑战,展现出更高的性能和稳定性。


📌 时间复杂度分析

在这里插入图片描述

在这里插入图片描述

快速排序算法的性能极大程度上取决于基准值(Pivot)的选择策略。

  • 最优情况:若每次分区操作都能均匀地将数据集切分为两部分,每部分包含近似相等数量的元素,则递归树的深度为log₂n。鉴于每一层递归涉及遍历数组,总体操作计数约为n * log₂n。因此,快速排序在最佳情况下的时间复杂度为O(n log n),表现出高度的效率。

  • 最差情况:相反,若每次选取的基准值都导致极不均衡的分区,极端情形下每次仅将数组分为一个元素和剩余所有元素两部分,这将导致递归深度上升至n,伴随每次遍历数组的操作,时间复杂度恶化为O(n²),与冒泡排序相近。

  • 平均情况:在实践中,若假定分区大致均匀,即每次都能将数据集分为两个大小相似的子集,快速排序的平均时间复杂度同样为O(n log n)。这对于多数随机分布数据集而言,是一个非常高效的排序方案。

鉴于最坏情况下的性能瓶颈,实际部署快速排序算法时,往往配合采用基准值优化策略,比如“三数取中法”,来增强其鲁棒性和普遍适用性,确保在多种数据条件下仍能保持高效的排序性能。


📌 总结

快速排序算法通过分治法策略实现高效排序,其核心包括选择基准值、分区操作及递归排序子序列三大步骤。为了进一步提升性能和适应不同场景,可采纳诸如三数取中法优化基准选择、小数组时切换至插入排序、尾递归优化及并行处理等策略。这些优化不仅能够减少最坏情况出现的概率,还充分利用现代计算资源,使快速排序在实践中表现得更为出色,成为处理大量数据排序任务的优选算法之一。

总之,快速排序凭借其高效与灵活性,在众多排序算法中占据重要地位,广泛应用于各种数据排序需求之中。


在这里插入图片描述

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

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

相关文章

基于Perfetto 解读一帧的生产消费流程 Android >= S Qualcomm

广告 首先帮我朋友打个广告 我们一起在运营一个视频号 感兴趣的可以帮忙点击右边这个小铃铛 铃铛 序 1.这个流程里面的东西如果展开其实是有很多的 内容其实还是比较浅显的 sf处就不贴源码了 关一个Vsync就有的解释 当然笔者在流程上先形成一个思维闭环 2.如有小伙伴需要 笔…

Java方法的递归

Java方法的递归 前言一、递归的概念示例代码示例 二、递归执行过程分析代码示例执行过程图 三、递归练习代码示例按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)递归求 1 2 3 ... 10写一个递归方法&#xff0c;输入一个非负整数&#xff0c;返回组成它的数字之和. …

零基础学Java第二十一天之IIO流之对象流

IO流之对象流 1、对象流 1、理解 将对象写入到文件&#xff0c;将文件里的对象读取到程序中 class ObjectInputStream – 对象输入流 class ObjectOutputStream – 对象输出流 序列化/钝化&#xff1a;程序里的对象 写入到 文件中 反序列化/活化&#xff1a;文件中的对象 读取…

【OpenCV实战】OpenCV实现自动调整亮度和对比度

一,基于局部直方图信息增强算法 对比度受限的自适应直方图均衡化(Contrast Limited Adaptive Histogram Equalization,简称CLAHE)是一种用于图像增强的技术,其原理主要基于自适应直方图均衡化(Adaptive Histogram Equalization,简称AHE)但增加了对比度限制来避免过度放…

uniapp蓝牙打印图片

前言 这是个蓝牙打印图片的功能&#xff0c;业务是打印界面固定的demo范围&#xff0c;这里通过html2canvas插件生成的图片base64&#xff0c;然后图片base64绘制到canvas中去后&#xff0c;获取canvas中的像素信息&#xff0c;然后对像素信息进行一个灰度值处理&#xff0c;灰…

在Linux系统中解决Java生成海报文字乱码和缺少字体文件的问题

在Linux系统中,如果缺少特定的字体文件,可以通过以下几种方法来解决: 1. 安装系统字体包 大多数Linux发行版提供了各种字体包,可以通过包管理器安装这些字体包。例如,在Debian/Ubuntu系统上,可以使用以下命令安装常见的字体包: # 安装基本的字体包 sudo apt-get updat…

Java集合的组内平均值怎么计算

要计算Java集合&#xff08;例如List或Set中的Integer、Double或其他数值类型的对象&#xff09;的组内平均值&#xff0c;我们需要遍历这个集合&#xff0c;累加所有的元素值&#xff0c;然后除以集合的大小&#xff08;即元素的数量&#xff09;。以下是一个详细的步骤说明和…

opencl色域变换,处理传递显存数据

在使用ffmpeg解码后的多路解码数据非常慢&#xff0c;还要给AI做行的加速方式是在显存处理数据&#xff0c;在视频拼接融合产品的产品与架构设计中&#xff0c;提出了比较可靠的方式是使用cuda&#xff0c;那么没有cuda的显卡如何处理呢 &#xff0c;比较好的方式是使用opencl来…

go语言的一些常见踩坑问题

开始之前&#xff0c;介绍一下​最近很火的开源技术&#xff0c;低代码。 作为一种软件开发技术逐渐进入了人们的视角里&#xff0c;它利用自身独特的优势占领市场一角——让使用者可以通过可视化的方式&#xff0c;以更少的编码&#xff0c;更快速地构建和交付应用软件&#…

安卓手机APP开发__网络连接性支持VPN

安卓手机APP开发__网络连接性支持VPN 安卓提供了API给开发者,来创建一个虚拟的私有网络(VPN)的解决方案. 根据这里的介绍,你能知道如何开发和测试你的针对安卓设备的VPN的客户端. 概述 VPN允许设备为了安全地连接网络,而没有物理性的连接在一个网络上. 安卓包括了一个内嵌的…

【无重复字符的最长子串】python,滑动窗口+哈希表

滑动窗口哈希表 哈希表 seen 统计&#xff1a; 指针 j遍历字符 s&#xff0c;哈希表统计字符 s[j]最后一次出现的索引 。 更新左指针 i &#xff1a; 根据上轮左指针 i 和 seen[s[j]]&#xff0c;每轮更新左边界 i &#xff0c;保证区间 [i1,j] 内无重复字符且最大。 更新结…

使用JSDOM安全截断文章HTML内容

在Web开发中&#xff0c;经常需要处理大量的HTML内容&#xff0c;尤其是在展示文章预览、动态加载内容或限制显示长度等场景中。直接截断HTML字符串可能会导致页面布局混乱、样式错误或标签不完整等问题。为了安全地截断HTML内容&#xff0c;我们可以利用jsdom库来解析HTML&…

JVM学习-垃圾回收器(一)

垃圾回收器 按线程数分类 串行垃圾回收器 串行回收是在同一时间段内只允许有一个CPU用于执行垃圾回收操作&#xff0c;此时工作线程被暂停&#xff0c;直至垃圾收集工作结束 在诸如单CPU处理器或者较小的应用内存等硬件平台不是特别优越的场合&#xff0c;串行回收器的性能表…

http和https的区别,怎么免费实现https(内涵教学)

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息&#xff0c;HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;如果攻击者截取了Web浏览器和网站服务器之间的传输报文&#xff0c;就可以直接读懂其中的信息&#xff0c;因此&…

etcd 和 MongoDB 的混沌(故障注入)测试方法

最近在对一些自建的数据库 driver/client 基础库的健壮性做混沌&#xff08;故障&#xff09;测试, 去验证了解业务的故障处理机制和恢复时长. 主要涉及到了 MongoDB 和 etcd 这两个基础组件. 本文会介绍下相关的测试方法. MongoDB 中的故障测试 MongoDB 是比较世界上热门的文…

AI网络爬虫:批量爬取电视猫上面的《庆余年》分集剧情

电视猫上面有《庆余年》分集剧情&#xff0c;如何批量爬取下来呢&#xff1f; 先找到每集的链接地址&#xff0c;都在这个class"epipage clear"的div标签里面的li标签下面的a标签里面&#xff1a; <a href"/drama/Yy0wHDA/episode">1</a> 这个…

速盾:负载均衡能防ddos攻击吗?

负载均衡是一种分布式系统的设计思想&#xff0c;通过将流量分散到多个服务器上&#xff0c;以提高系统的稳定性和可扩展性。然而&#xff0c;负载均衡本身并不能完全防止DDoS攻击&#xff0c;但可以在一定程度上减轻其影响。 DDoS&#xff08;分布式拒绝服务&#xff09;攻击…

【C语言】8.C语言操作符详解(1)

文章目录 1.操作符的分类2.⼆进制和进制转换3.原码、反码、补码4.移位操作符4.1 左移操作符4.2 右移操作符 5.位操作符&#xff1a;&、|、^、~5.1 &&#xff1a;按位与5.2 |&#xff1a;按位或5.3 ^&#xff1a;按位异或5.4 ~&#xff1a;按位取反5.5 例题例题1例题2例…

短视频矩阵系统4年独立开发正规代发布接口源码搭建部署开发

1. 短视频矩阵源码技术开发要求及实现流程&#xff1a; 短视频矩阵源码开发要求具备视频录制、编辑、剪辑、分享等基本功能&#xff0c;支持实时滤镜、特效、音乐等个性化编辑&#xff0c;能够实现高效的视频渲染和处理。开发流程主要包括需求分析、技术选型、设计架构、编码实…

Web前端开发技术、详细文章、(例子)html 列表、有序列表、无序列表、列表嵌套

目录 列表概述 列表类型与标记符号 无序列表 语法&#xff1a; 语法说明&#xff1a; 无序列表标记的 type 属性及其说明 代码解释 有序列表 基本语法 属性说明 1、列表 o1标记的属性 2、列表项li标记的属性 有序列表 o1标记的属性、值 代码解释 列表嵌套 基本…