【高效且应用广泛的排序 —— 快速排序算法】

news/2024/9/28 8:20:52/
cle class="baidu_pl">
cle_content" class="article_content clearfix">
content_views" class="markdown_views prism-atom-one-dark">cap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

高效且应用广泛的排序 —— 快速class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法

快速排序是一种常用的class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法࿰c;主要采用分治的思想。以下是对快速class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法的详细介绍及代码示例:

快速排序的基本思路是࿰c;每次将一个位置上的数据归位࿰c;使得该数左边的所有数据都比该数小࿰c;右边所有的数据都比该数大c;然后递归将已归位的数据左右两边再次进行快排࿰c;从而实现所有数据的归位。

class="tags" href="/SuanFa.html" title=算法>算法步骤如下:

  1. 从数列中挑出一个元素࿰c;称为“基准”。
  2. 重新排序数列࿰c;所有比基准值小的元素摆放在基准前面࿰c;所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后࿰c;该基准就处于数列的中间位置。这个称为分区操作。
  3. 递归地把小于基准值元素的子数列和大于基准值元素的子数列排序。

c="https://i-blog.csdnimg.cn/direct/07a10405d08c4868b61da0859c0a8390.gif#pic_center" alt="在这里插入图片描述" />

例如࿰c;对于数组࿰c;选择最左边的元素 29 作为中间点元素࿰c;然后将数组分成三部分:(0, 14, 15, 20, 25),(29),(44, 37)。中间节点 29 已经排好序了࿰c;不需要处理。接下来再对左右分别进行快速排序。

下面是 Java 代码示例:

<code class="prism language-class="tags" href="/JAVA.html" title=java>java">class="token keyword">public class="token keyword">class class="token class-name">QuickSort class="token punctuation">{class="token comment">// 测试class="token keyword">public class="token keyword">static class="token keyword">void class="token function">mainclass="token punctuation">(class="token class-name">Stringclass="token punctuation">[class="token punctuation">] argsclass="token punctuation">) class="token punctuation">{class="token keyword">intclass="token punctuation">[class="token punctuation">] arr class="token operator">= class="token punctuation">{class="token number">5class="token punctuation">,class="token number">2class="token punctuation">,class="token number">9class="token punctuation">,class="token number">6class="token punctuation">,class="token number">3class="token punctuation">,class="token number">1class="token punctuation">,class="token number">7class="token punctuation">,class="token number">8class="token punctuation">,class="token number">4class="token punctuation">}class="token punctuation">;class="token keyword">int left class="token operator">= class="token number">0class="token punctuation">;class="token keyword">int right class="token operator">= arrclass="token punctuation">.length class="token operator">- class="token number">1class="token punctuation">;class="token function">quickSortclass="token punctuation">(arrclass="token punctuation">, leftclass="token punctuation">, rightclass="token punctuation">)class="token punctuation">;class="token class-name">Systemclass="token punctuation">.outclass="token punctuation">.class="token function">printlnclass="token punctuation">(class="token string">"排序结果:"class="token punctuation">)class="token punctuation">;class="token keyword">forclass="token punctuation">(class="token keyword">int num class="token operator">: arrclass="token punctuation">)class="token punctuation">{class="token class-name">Systemclass="token punctuation">.outclass="token punctuation">.class="token function">printclass="token punctuation">(num class="token operator">+ class="token string">" "class="token punctuation">)class="token punctuation">;class="token punctuation">}class="token punctuation">}class="token comment">// 快速class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法class="token keyword">public class="token keyword">static class="token keyword">void class="token function">quickSortclass="token punctuation">(class="token keyword">intclass="token punctuation">[class="token punctuation">] arrclass="token punctuation">,class="token keyword">int leftclass="token punctuation">,class="token keyword">int rightclass="token punctuation">) class="token punctuation">{class="token keyword">ifclass="token punctuation">(left class="token operator">< rightclass="token punctuation">) class="token punctuation">{class="token keyword">int partitionIndex class="token operator">= class="token function">partitionclass="token punctuation">(arrclass="token punctuation">, leftclass="token punctuation">, rightclass="token punctuation">)class="token punctuation">;class="token comment">// 将数组划分为两部分࿰c;并返回划分点的索引class="token function">quickSortclass="token punctuation">(arrclass="token punctuation">, leftclass="token punctuation">, partitionIndex class="token operator">- class="token number">1class="token punctuation">)class="token punctuation">;class="token comment">// 递归排序左子数组class="token function">quickSortclass="token punctuation">(arrclass="token punctuation">, partitionIndex class="token operator">+ class="token number">1class="token punctuation">, rightclass="token punctuation">)class="token punctuation">;class="token comment">// 递归排序右子数组class="token punctuation">}class="token punctuation">}class="token comment">// 划分函数class="token keyword">public class="token keyword">static class="token keyword">int class="token function">partitionclass="token punctuation">(class="token keyword">intclass="token punctuation">[class="token punctuation">] arrclass="token punctuation">,class="token keyword">int leftclass="token punctuation">,class="token keyword">int rightclass="token punctuation">) class="token punctuation">{class="token keyword">int pivot class="token operator">= arrclass="token punctuation">[rightclass="token punctuation">]class="token punctuation">;class="token comment">// 选择最后一个元素作为基准值class="token keyword">int i class="token operator">= left class="token operator">- class="token number">1class="token punctuation">;class="token comment">// i 为较小元素的索引class="token keyword">forclass="token punctuation">(class="token keyword">int j class="token operator">= leftclass="token punctuation">; j class="token operator">< rightclass="token punctuation">; jclass="token operator">++class="token punctuation">)class="token punctuation">{class="token keyword">ifclass="token punctuation">(arrclass="token punctuation">[jclass="token punctuation">] class="token operator"><= pivotclass="token punctuation">)class="token punctuation">{iclass="token operator">++class="token punctuation">;class="token function">swapclass="token punctuation">(arrclass="token punctuation">, iclass="token punctuation">, jclass="token punctuation">)class="token punctuation">;class="token comment">// 交换较小元素与当前元素class="token punctuation">}class="token comment">// 如果数组元素大于等于 middleValue࿰c;则继续向后遍历࿰c;middleIndex 值不变class="token punctuation">}class="token function">swapclass="token punctuation">(arrclass="token punctuation">, i class="token operator">+ class="token number">1class="token punctuation">, rightclass="token punctuation">)class="token punctuation">;class="token comment">// 将基准值放置到正确的位置上class="token keyword">return i class="token operator">+ class="token number">1class="token punctuation">;class="token comment">// 返回划分点的索引class="token punctuation">}class="token comment">// 交换数组中两个元素的位置class="token keyword">public class="token keyword">static class="token keyword">void class="token function">swapclass="token punctuation">(class="token keyword">intclass="token punctuation">[class="token punctuation">] arrclass="token punctuation">,class="token keyword">int iclass="token punctuation">,class="token keyword">int jclass="token punctuation">) class="token punctuation">{class="token keyword">int temp class="token operator">= arrclass="token punctuation">[iclass="token punctuation">]class="token punctuation">;arrclass="token punctuation">[iclass="token punctuation">]class="token operator">= arrclass="token punctuation">[jclass="token punctuation">]class="token punctuation">;arrclass="token punctuation">[jclass="token punctuation">]class="token operator">= tempclass="token punctuation">;class="token punctuation">}
class="token punctuation">}
code>

快速排序在平均情况下时间复杂度为 O(n log n)c;但在最坏情况下时间复杂度会变为 O(n²)。可以通过随机选择基准元素或使用三数取中法等方法来避免最坏情况。空间复杂度为 O(log n)c;因为在递归调用中需要使用栈来存储中间结果。快速排序虽然在最坏情况下时间复杂度可能较高࿰c;但在实际应用中通常表现良好࿰c;尤其对于大规模数据集的排序。如果实现得当࿰c;它是一种高效的class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法。

快速class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法基本思路

c="https://i-blog.csdnimg.cn/direct/1b7a8563cc3941ecb2106f462445e84c.png" alt="在这里插入图片描述" />

快速排序是一种高效的class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法࿰c;其基本思路是分治法。首先从数列中取出一个数作为基准数࿰c;然后进行分区过程࿰c;将比这个数大的数全放到它的右边࿰c;小于或等于它的数全放到它的左边。接着再对左右区间重复这个步骤࿰c;直到各区间只有一个数。概括来说就是“挖坑填数+分治法”。

快速排序就像整理一堆杂乱的卡片。假设我们有一叠无序的数字卡片࿰c;我们随机选取一张卡片作为基准数࿰c;比如选择第一张卡片。然后我们从这叠卡片的右边开始࿰c;找到比基准数小的卡片࿰c;从左边开始找到比基准数大的卡片࿰c;找到后交换这两张卡片的位置。这样不断进行࿰c;直到左右指针相遇。此时࿰c;我们把基准数放到正确的位置上࿰c;这个位置左边的数字都小于等于基准数࿰c;右边的数字都大于基准数。然后我们再对左右两边的子序列重复这个过程࿰c;直到所有的子序列都只有一个数字࿰c;此时整个序列就有序了。

例如࿰c;有一组数字。我们选择4作为基准数࿰c;首先从右边开始࿰c;9大于4࿰c;继续向左࿰c;3小于4࿰c;此时停下。然后从左边开始࿰c;1小于4࿰c;继续向右࿰c;6大于4࿰c;停下。交换3和6的位置࿰c;得到。接着继续这个过程࿰c;指针不断移动࿰c;直到左右指针相遇。最后把4放到正确的位置上࿰c;此时序列变为。然后再对左边的子序列和右边的子序列分别进行快速排序࿰c;直到所有子序列都有序。

快速class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法步骤

  1. 从序列中任选一个数作为基准数࿰c;一般就使用第一个数。
  2. 进行分区操作࿰c;将大于基准数的数放在右边࿰c;小于基准数的数放在左边࿰c;注意一定要先右后左。具体操作是设置两个指针i和j࿰c;分别指向数组的第一个元素和最后一个元素。从j开始向左挪动࿰c;直到找到一个小于基准数的数停下来;然后切换到i再向右挪动࿰c;直到找到一个数大于基准数的数停下来。最后将i和j指向的元素进行交换位置。重复这个过程直到i和j重合࿰c;此时把重合点的元素与基准元素交换位置。
  3. 对左右区间分别执行上述步骤࿰c;直到每个区间只有一个数为止。

例如对于数组࿰c;选择5作为基准数。首先j从6开始向左移动࿰c;找到3小于5停下。i从0开始向右移动࿰c;找到8大于5停下。交换3和8的位置࿰c;得到。接着j继续向左移动࿰c;找到1小于5停下。i继续向右移动࿰c;找到2小于5不停下࿰c;继续移动找到8大于5停下。交换1和8的位置࿰c;得到。此时i和j重合在1的位置࿰c;把1和5交换位置࿰c;得到。然后对左边的子序列和右边的子序列分别进行快速排序࿰c;直到所有子序列都有序。

快速排序代码示例解析

以下是一段快速排序的 Python 代码示例:

<code class="prism language-class="tags" href="/PYTHON.html" title=python>python">class="token keyword">def class="token function">quickSortclass="token punctuation">(listsclass="token punctuation">, leftclass="token punctuation">, rightclass="token punctuation">)class="token punctuation">:class="token comment"># 快速排序class="token keyword">if left class="token operator">>= rightclass="token punctuation">:class="token keyword">return listskey class="token operator">= listsclass="token punctuation">[leftclass="token punctuation">]low class="token operator">= lefthigh class="token operator">= rightclass="token keyword">while left class="token operator">< rightclass="token punctuation">:class="token keyword">while left class="token operator">< right class="token keyword">and listsclass="token punctuation">[rightclass="token punctuation">] class="token operator">>= keyclass="token punctuation">:right class="token operator">-= class="token number">1listsclass="token punctuation">[leftclass="token punctuation">] class="token operator">= listsclass="token punctuation">[rightclass="token punctuation">]class="token keyword">while left class="token operator">< right class="token keyword">and listsclass="token punctuation">[leftclass="token punctuation">] class="token operator"><= keyclass="token punctuation">:left class="token operator">+= class="token number">1listsclass="token punctuation">[rightclass="token punctuation">] class="token operator">= listsclass="token punctuation">[leftclass="token punctuation">]listsclass="token punctuation">[rightclass="token punctuation">] class="token operator">= keyclass="token keyword">printclass="token punctuation">(class="token string">"lists:"class="token punctuation">, listsclass="token punctuation">)quickSortclass="token punctuation">(listsclass="token punctuation">, lowclass="token punctuation">, left class="token operator">- class="token number">1class="token punctuation">)quickSortclass="token punctuation">(listsclass="token punctuation">, left class="token operator">+ class="token number">1class="token punctuation">, highclass="token punctuation">)class="token keyword">return lists
code>

这段代码首先判断左右指针的位置࿰c;如果左指针大于等于右指针࿰c;说明该子序列已经有序࿰c;直接返回。然后选择左指针指向的元素作为基准数<code>keycode>。接着使用两个循环࿰c;从右向左找到小于基准数的元素࿰c;从左向右找到大于基准数的元素࿰c;然后交换这两个元素的位置。当左右指针相遇时࿰c;将基准数放到正确的位置上。最后递归地对左右子序列进行快速排序。

以列表为例࿰c;调用<code>quickSortcode>函数进行排序。首先选择3作为基准数࿰c;从右向左找到2小于3停下࿰c;从左向右找到6大于3停下࿰c;交换2和6的位置࿰c;得到。接着继续这个过程࿰c;直到左右指针相遇࿰c;把3放到正确的位置上。然后对左边的子序列和右边的子序列分别进行快速排序࿰c;直到整个列表有序。

快速排序时间复杂度分析

快速排序的时间复杂度与划分是否平衡密切相关。最优情况下࿰c;时间复杂度为 O(nlogn)。这是因为每次划分都能将序列均匀地分成两部分࿰c;此时快速排序的性能与归并排序一样。

例如࿰c;对于一个包含 n 个数的序列࿰c;递归的第一层࿰c;将 n 个数划分为 2 个子区间࿰c;每个子区间的数字个数约为 n/2;递归的第二层࿰c;将 n 个数划分为 4 个子区间࿰c;每个子区间的数字个数约为 n/4;以此类推࿰c;递归的第 logn 层࿰c;将 n 个数划分为 n 个子区间࿰c;每个子区间的数字个数为 1。在每一层的划分过程中࿰c;时间复杂度约为 O(n)࿰c;而总共需要划分 logn 层࿰c;所以最优情况下的时间复杂度为 O(nlogn)。

然而࿰c;在最坏情况下࿰c;时间复杂度为 O(n^2)。当每次选择的基准元素是最大或最小元素时࿰c;快速排序的时间复杂度是 O(n^2)。例如࿰c;序列已经是有序的࿰c;每次选择第一个元素作为基准数࿰c;那么每次划分只能分出一个元素࿰c;导致递归的深度达到 n࿰c;而每一层的时间复杂度为 O(n)࿰c;所以最坏情况下的时间复杂度为 O(n^2)。

平均情况下࿰c;快速排序的时间复杂度也是 O(nlogn)。在实际应用中࿰c;快速排序通常表现良好࿰c;尤其对于大规模数据集的排序。

快速排序避免最坏情况方法

为了避免快速排序的最坏情况࿰c;可以采用以下几种方法:

  1. 随机选取划分点:每次随机从待排序序列中选取一个元素作为划分点࿰c;从而降低最坏情况的概率。这样可以避免每次都选择到最大或最小元素作为基准数࿰c;使得划分更加平衡。
  2. 三数取中法:选取待排序序列的第一个、中间一个、最后一个元素中的中间值作为划分点࿰c;从而降低最坏情况的概率。例如对于序列࿰c;可以选择1、6、3中的中间值3作为基准数࿰c;这样可以在一定程度上避免选择到最大或最小元素。
  3. 在递归深度较浅时采用其他class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法࿰c;比如插入排序或归并排序等࿰c;从而避免快速排序退化成 O(n^2) 的情况。当问题规模小于某一 k 值时࿰c;采用插入排序࿰c;提高class="tags" href="/SuanFa.html" title=算法>算法效率。

总结

快速class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法是一种高效的class="tags" href="/PaiXuSuanFa.html" title=排序class="tags" href="/SuanFa.html" title=算法>算法>排序class="tags" href="/SuanFa.html" title=算法>算法࿰c;其基本思路是分治法࿰c;通过不断地划分和递归࿰c;将序列分成越来越小的子序列进行排序࿰c;直到所有子序列都有序。在实际应用中࿰c;可以根据不同的情况选择合适的方法来避免最坏情况的发生࿰c;以提高class="tags" href="/SuanFa.html" title=算法>算法的性能。


http://www.ppmy.cn/news/1531085.html

相关文章

pdf转换成word有哪些方法?10种将PDF转成word的方法

pdf转换成word有哪些方法&#xff1f;在数字化世界中&#xff0c;PDF和word文档是最常用的两种文件格式。PDF凭借其固定布局和跨平台的兼容性&#xff0c;成为了文件分享的首选&#xff0c;而word则因其灵活的编辑功能被广泛应用于各种文本处理需求。在许多情况下&#xff0c;我…

《论软件系统架构风格》写作框架,软考高级系统架构设计师

论文真题 系统架构风格&#xff08;System Architecture Style&#xff09;是描述某一特定应用领域中系统组织方式的惯用模式。架构风格定义了一个词汇表和一组约束&#xff0c;词汇表中包含一些构件和连接件类型&#xff0c;而这组约束指出系统是如何将这些构件和连接件组合起…

探索 Snowflake 与 Databend 的云原生数仓技术与应用实践 | Data Infra NO.21 回顾

上周六&#xff0c;第二十一期「Data Infra 研究社」在线上与大家相见。活动邀请到了西门子数据分析师陈砚林与 Databend 联合创始人王吟&#xff0c;为我们带来了一场关于 Snowflake 和 Databend 的技术探索。Snowflake&#xff0c;这个市值曾超过 700 亿美元的云原生数据仓库…

云手机群控怎么用?有什么优势?

群控系统&#xff0c;顾名思义&#xff0c;是用于批量控制多部手机的工具&#xff0c;能够通过计算机或客户端同时管理多台设备。借助群控系统&#xff0c;用户可以在电脑上操作多部手机&#xff0c;模拟真实操作场景&#xff0c;从而大幅提升工作效率&#xff0c;并有效控制管…

Snap AR眼镜Spectacles的技术揭秘:通往真正AR体验的道路

Snap公司自2010年成立以来&#xff0c;一直致力于探索增强现实&#xff08;AR&#xff09;技术的边界。经过多年的研发与迭代&#xff0c;Snap终于在最新一代Spectacles中实现了重大突破&#xff0c;为用户带来了前所未有的沉浸式AR体验。本文将深入探讨Spectacles的发展历程、…

docker面经

docker面经在线链接 docker面经在线链接&#x1f517;&#xff1a; (https://h03yz7idw7.feishu.cn/wiki/N3CVwO3kMifLypkJqnic9wNynKh)

音视频整体解码流程和同步流程

目录 1. 整体解码流程1. 初始化 FFmpeg2. 打开媒体文件3. 查找解码器4. 打开解码器5. 读取和解码数据6. 处理解码后的帧7. 释放资源 2. 音视频同步整体流程1. 解复用媒体流2. 解码3. 以音频为时钟源进行音视频同步的策略4. 缓冲区设计 现在先说大体流程&#xff0c;不分析代码 …

useImperativeHandle, forwardRef ,使方法和属性应暴露给父组件

useImperativeHandle 是 React 中的一个 Hook&#xff0c;用于自定义组件的实例值。它通常与 forwardRef 一起使用&#xff0c;允许你在父组件中通过引用&#xff08;ref&#xff09;访问子组件的特定实例方法或属性。以下是对 useImperativeHandle 的详细解析。 1、语法 impo…