快速排序的实现思路

简介: 快速排序(Quicksort)由托尼·霍尔于1960年提出,是一种高效的分治排序算法。其核心思想是通过选取基准元素将数组划分为两部分,递归地对左右子数组排序。算法平均时间复杂度为 $O(n \log n)$,具有原地排序、空间利用率高等优点,广泛应用于大数据排序场景。合理选择基准和优化策略可显著提升性能,是实际应用中最常用的排序算法之一。

快速排序(Quicksort)由托尼·霍尔在1960年提出,是一种分治策略的高效排序算法,其核心思想是通过选择基准元素将数组分为两部分,递归地对两部分进行排序。以下是详细的实现思路和示例说明:

基本原理

  1. 分治法(Divide and Conquer)
    • 分解:选择一个基准元素(pivot),将数组分为两部分,使得左边部分的元素都小于等于基准值,右边部分的元素都大于等于基准值。
    • 解决:递归地对左右两部分分别进行快速排序。
    • 合并:由于左右两部分已经有序且基准元素位于正确位置,无需额外合并操作。

实现步骤

  1. 选择基准元素(Pivot):从数组中选择一个元素作为基准值。常见的选择方法有:
    • 选择第一个元素。
    • 选择最后一个元素。
    • 选择中间元素。
    • 随机选择或三数取中法(选择首、中、尾三个元素的中间值)。
  2. 分区操作(Partition)
    • 将基准元素放到数组末尾(如果选择最后一个元素为基准,则无需移动)。
    • 使用双指针法,将小于基准的元素交换到左边,大于基准的元素留在右边。
    • 最后将基准元素放到正确的位置(左边部分的末尾)。
  3. 递归排序:对左右两部分分别重复上述步骤,直到子数组长度为1或0。

示例演示

假设我们要对数组 [5, 3, 8, 4, 6] 进行升序排序,选择最后一个元素 6 作为基准:

  1. 初始状态[5, 3, 8, 4, 6](基准=6)
  2. 分区操作
    • 左指针 i 从0开始,右指针 j 从0到倒数第二个元素遍历。
    • j=05 < 6,交换 55i=1,数组:[5, 3, 8, 4, 6]
    • j=13 < 6,交换 33i=2,数组:[5, 3, 8, 4, 6]
    • j=28 > 6,不变,数组:[5, 3, 8, 4, 6]
    • j=34 < 6,交换 84i=3,数组:[5, 3, 4, 8, 6]
    • 最后交换基准 68,数组:[5, 3, 4, 6, 8]
  3. 递归排序
    • 左子数组 [5, 3, 4](基准=4)→ 排序后:[3, 4, 5]
    • 右子数组 [8](无需排序)
  4. 最终结果[3, 4, 5, 6, 8]

代码实现

以下是快速排序的Python实现(选择最后一个元素为基准):

def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    else:
        pivot = arr[-1]
        left = [x for x in arr[:-1] if x <= pivot]
        right = [x for x in arr[:-1] if x > pivot]
        return quick_sort(left) + [pivot] + quick_sort(right)

# 测试
arr = [5, 3, 8, 4, 6]
print("排序前:", arr)
print("排序后:", quick_sort(arr))

原地分区版本(节省空间):

def partition(arr, low, high):
    pivot = arr[high]
    i = low - 1
    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i + 1], arr[high] = arr[high], arr[i + 1]
    return i + 1

def quick_sort_inplace(arr, low, high):
    if low < high:
        pi = partition(arr, low, high)
        quick_sort_inplace(arr, low, pi - 1)
        quick_sort_inplace(arr, pi + 1, high)

# 测试
arr = [5, 3, 8, 4, 6]
quick_sort_inplace(arr, 0, len(arr) - 1)
print("排序后:", arr)

复杂度分析

  • 时间复杂度
    • 最好情况:每次分区均匀,时间复杂度为 $O(n \log n)$。
    • 平均情况:时间复杂度为 $O(n \log n)$。
    • 最坏情况:每次分区极不均匀(如已排序数组选择第一个元素为基准),时间复杂度为 $O(n^2)$。
  • 空间复杂度
    • 非原地版本:$O(n)$(递归栈深度)。
    • 原地版本:$O(\log n)$(递归栈深度,平均情况)。
  • 稳定性:快速排序是不稳定的,因为分区过程中可能交换相等元素的顺序。

优化策略

  1. 随机选择基准:避免最坏情况的概率,提高算法稳定性。
  2. 三数取中法:选择首、中、尾三个元素的中间值作为基准。
  3. 小数组使用插入排序:当子数组长度较小时(如小于10),使用插入排序更高效。
  4. 三路快排:将数组分为小于、等于、大于基准的三部分,适用于包含大量重复元素的数据。

适用场景

  • 大数据集:快速排序在平均情况下效率极高,适用于大规模数据排序。
  • 原地排序:原地版本只需 $O(\log n)$ 空间,适合内存有限的场景。
  • 通用排序:标准库(如Python的sorted())常采用优化后的快速排序。

对比其他排序

  • 比插入排序快:对于大规模数据,快速排序的 $O(n \log n)$ 明显优于插入排序的 $O(n^2)$。
  • 比归并排序省空间:原地快排空间复杂度更低($O(\log n)$ vs $O(n)$)。
  • 比堆排序快:快速排序的常数因子更小,实际性能更优。

快速排序因其高效性和灵活性,成为最常用的排序算法之一。合理选择基准和优化策略能进一步提升其性能。

目录
相关文章
|
缓存 安全 Java
为什么全局变量可能成为多线程环境中的安全隐患
为什么全局变量可能成为多线程环境中的安全隐患
|
3月前
|
存储 前端开发 关系型数据库
终于有人把数据仓库讲明白了
数据仓库不是大号数据库,更不是BI附属品。它通过整合多源数据、统一标准,让数据更易查、易用,真正服务于业务分析与决策。本文带你厘清数据仓库的本质、架构与搭建步骤,避开常见误区,实现数据价值最大化。
终于有人把数据仓库讲明白了
|
存储 搜索推荐 算法
快速排序算法详解
快速排序算法详解
|
监控 编译器 C语言
【C语言】inline 关键字详解
`inline` 关键字是C语言中的一个有用工具,通过消除函数调用的开销来提高执行效率。然而,它并不是万能的,应该根据具体情况慎重使用,以避免代码膨胀和其他潜在问题。
549 1
|
边缘计算 人工智能 安全
探索边缘计算:定义、优势及未来趋势
探索边缘计算:定义、优势及未来趋势
|
算法 计算机视觉 Docker
Docker容器中的OpenCV:轻松构建可移植的计算机视觉环境
Docker容器中的OpenCV:轻松构建可移植的计算机视觉环境
332 3
Docker容器中的OpenCV:轻松构建可移植的计算机视觉环境
|
数据库 SQL Oracle
数据库漫谈-sybase
sybase就是“system”加“database”
|
搜索推荐 算法 索引
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
611 4
【循环链表】数据结构——单向循环链表和双向循环链表操作&笔记
【循环链表】数据结构——单向循环链表和双向循环链表操作&笔记
|
搜索推荐 算法 Java
【数据结构排序算法篇】----快速排序【实战演练】
【数据结构排序算法篇】----快速排序【实战演练】
195 2

热门文章

最新文章