排序算法(七):快速排序

简介: 快速排序是通过分治的方式,根据选定元素将待排序集合拆分为两个值域的子集合,并对子集合递归拆分,当拆分后的每个子集合中元素个数为一时,自然就是有序状态。

快速排序是通过分治的方式,根据选定元素将待排序集合拆分为两个值域的子集合,并对子集合递归拆分,当拆分后的每个子集合中元素个数为一时,自然就是有序状态。

归并排序也是基于分治的思想,不过归并流程是将子集合合并成为有序的集合,递归执行来完成整个集合的排序。快速排序的分治流程是根据选定元素,将集合分隔为两个子集合,一个子集合中所有元素不大于选定元素值,另一个子集合中所有元素不小于选定元素值,则用于拆分集合的选定元素即为已排序元素。即每次拆分都会形成一个已排序元素,所以 N 个元素的序列,拆分的次数级别为 O(N)。将拆分过程类比为二叉树形式,考虑普通二叉树和斜树的情况,则二叉树高度级别为 O(log_2N)~O(N)

算法过程

  1. 在所有集合中均选定某一个元素;
  2. 根据选定元素,将每个集合拆分为元素值不大于该元素值的子集合,和元素值不小于该元素值的子集合;
  3. 重复步骤 1,2,直到每个集合中元素个数为 1。

演示示例

假设每个集合中的选定元素 Tr 为集合中的最后一个元素。

拆分过程

拆分过程也就是将集合中元素值不大于 Tr 的元素,和元素值不小于 Tr 的元素,通过交换元素位置的方式分别移动到 Tr 元素的两侧,这里不妨称之为正确区域。由此可知,在拆分过程中,若已将集合中所有小于 Tr 值的元素移动到正确区域中,则拆分过程完成。

如下示例中 B1B2 元素值不小于 TrS1S2S3 元素值小于 Tr

在集合由左向右的遍历过程中,若当前元素值小于 Tr 值时,则将当前元素替换到正确区域中。所以在拆分过程中需要维持两个变量 indexindex_{right},分别指向当前遍历的元素位置,和正确区域尾部的下一个元素位置,或者称之为带加入正确区域的元素位置。

首次访问: index = 0index_{right} = 0,皆指向 S1
此时正确区域为空,所以正确区域尾部的下一个元素位置也就是起始元素位置

因为 S1 值小于 Tr,所以替换 indexindex_{right} 指向的元素值(其实不用替换,就是同一个元素),移动 indexindex_{right} 各自指向下一个元素位置。

第 2 次访问: index = 1index_{right} = 1,皆指向 B1
此时正确区域元素为:[S1],所以正确区域尾部的下一个元素就是 B1

B1 元素值不小于 Tr,所以 index 指向下一个元素,index_{right} 指向不变

第 3 次访问: index = 2,指向 B2index_{right} = 1,指向 B1
此时正确区域元素为:[S1],所以正确区域尾部的下一个元素就是 B1

B2 元素值不小于 Tr,所以 index 指向下一个元素,index_{right} 指向不变

第 4 次访问: index = 3,指向 S2index_{right} = 1,指向 B1
此时正确区域元素为:[S1],所以正确区域尾部的下一个元素就是 B1

S2 元素值小于 Tr,所以替换 indexindex_{right} 指向的元素值,移动 indexindex_{right} 各自指向下一个元素位置。

第 5 次访问: index = 4,指向 S3index_{right} = 2,指向 B2
此时正确区域元素为:[S1, S2],所以正确区域尾部的下一个元素就是 B2

S3 元素值小于 Tr,所以替换 indexindex_{right} 指向的元素值,移动 indexindex_{right} 各自指向下一个元素位置。

第 6 次访问: index = 5,指向 Trindex_{right} = 3,指向 B1
此时正确区域元素为:[S1, S2, S3],所以正确区域尾部的下一个元素就是 B1

因为访问到了集合尾部的选定元素,此时替换 indexindex_{right} 指向的元素值,完成拆分过程。

此时可以发现,选定元素的左右两侧皆为正确区域,即左侧元素值都不大于 Tr 值,右侧元素值都不小于 Tr 值。所以下一轮进行拆分的则为 [S1, S2, S3] 构成的集合和 [B2, B1] 构成的集合。

拆分过程存在一种现象,例如当前情况下是取集合的最后一个元素为选定元素进行拆分,若初始序列为有序状态,则每一次拆分后的两个集合,一个集合元素个数为 N-1,另一个集合为空,递归进行拆解时情况同样如此,也就是走势宛如斜树一般。

算法示例

def sort(arr, left, right):
    if left < right:
        divide = quickSort(arr, left, right)   # the divide operation
        sort(arr, left, divide - 1)   # recursive sorting
        sort(arr, divide + 1, right)

def quickSort(arr, left, right):
    lessIndex, partitionValue = left, arr[right]
    for i in range(left, right):  #Traversing
        if arr[i] < partitionValue:
            arr[i], arr[lessIndex] = arr[lessIndex], arr[i]
            lessIndex = lessIndex + 1
    arr[lessIndex], arr[right] = partitionValue, arr[lessIndex]
    return lessIndex

quickSort 操作中的循环用于遍历集合中元素,每一次遍历结束,可以形成两个正确区域,即不大于选定元素值的元素区域,和不小于选定元素值得元素区域。因为是直接根据位置进行替换,所以相比较于两两相邻元素比较替换效率要高许多,当然也导致了算法的不稳定性。

算法分析

快速排序是一种非稳定排序算法,形式上类似于归并排序,操作上刚好相反,一个是对集合先拆分后操作,一个是对集合先操作后拆分。对于 N 个元素的初始集合,因为在每个子集合的拆分过程中,都需要对集合进行遍历比较,所以若对 K 个元素的集合进行拆分,则比较次数级别为 O(K),平均交换次数为 \frac K2,即交换次数级别为 O(K)。因为拆分集合的过程存在普通二叉树和斜树的情况,所以树高为 log_2N~N,每一层的平均比较和交换复杂度为 O(N),所以累加可得快速排序的比较和交换复杂度为 Nlog_2N ~ N^2。排序过程属于原地排序,不需要额外的存储空间,所以空间复杂度为 O(log_2N)~O(N)

github 链接:快速排序

相关文章
|
8天前
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
30 4
|
1月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
20 1
|
1月前
|
搜索推荐 Java Go
深入了解快速排序算法
深入了解快速排序算法
31 2
|
1月前
|
存储 搜索推荐 算法
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
|
1月前
|
算法 Python
Python算法编程:冒泡排序、选择排序、快速排序
Python算法编程:冒泡排序、选择排序、快速排序
|
1月前
|
搜索推荐 C语言 C++
【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)
【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)
|
3月前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
39 1
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
3月前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
|
4月前
|
算法 搜索推荐 编译器
算法高手养成记:Python快速排序的深度优化与实战案例分析
【7月更文挑战第11天】快速排序是编程基础,以O(n log n)时间复杂度和原址排序著称。其核心是“分而治之”,通过选择基准元素分割数组并递归排序两部分。优化包括:选择中位数作基准、尾递归优化、小数组用简单排序。以下是一个考虑优化的Python实现片段,展示了随机基准选择。通过实践和优化,能提升算法技能。**
56 3
|
5月前
|
搜索推荐 算法 Java
Java中的快速排序、归并排序和堆排序是常见的排序算法。
【6月更文挑战第21天】Java中的快速排序、归并排序和堆排序是常见的排序算法。快速排序采用分治,以基准元素划分数组并递归排序;归并排序同样分治,先分割再合并有序子数组;堆排序通过构建堆来排序,保持堆性质并交换堆顶元素。每种算法各有优劣:快排平均高效,最坏O(n²);归并稳定O(n log n)但需额外空间;堆排序O(n log n)且原地排序,但不稳定。
46 3