菜单

排序与搜索算法

相关源文件

本文档提供了TheAlgorithms/Python仓库中排序和搜索算法实现的详细信息。这些算法是基本计算过程,它们要么按特定顺序排列元素(排序),要么在集合中定位特定元素(搜索)。该仓库包含各种算法实现,每种算法都有不同的性能特征、策略和用例。

算法概述

TheAlgorithms/Python仓库包含经典排序和搜索算法的实现。排序实现位于sorts/目录中,而搜索实现位于searches/目录中。所有实现都为处理可比较项的集合提供了统一的接口。

核心算法类别

函数级别实现映射

来源

搜索算法

搜索算法在集合中定位特定元素。该仓库实现了具有各种优化和接口的顺序搜索和二分搜索策略。

线性搜索算法

线性搜索顺序检查每个元素,直到找到目标或耗尽集合。该仓库提供了迭代和递归实现。

线性搜索实现

linear_search()函数在searches/linear_search.py12-33提供了直接的实现

  • 找到目标元素(如果存在)的索引
  • 如果元素不存在,则返回-1
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

rec_linear_search()函数在searches/linear_search.py36-65提供了递归方法

  • 同时从数组的两端进行搜索
  • 每次递归调用都减小搜索空间
  • 包含边界检查以提高安全性

来源

二分搜索算法

二分搜索算法在已排序的集合上工作,重复地将搜索区间分成两半。该仓库提供了具有不同接口和优化的多个二分搜索实现。

核心二分搜索函数

searches/binary_search.py 模块实现了几种二分搜索变体

  1. binary_search() searches/binary_search.py181-215 - 基本迭代实现
  2. binary_search_std_lib() searches/binary_search.py218-243 - 使用 Python 的bisect模块
  3. binary_search_by_recursion() searches/binary_search.py246-283 - 递归实现
  4. exponential_search() searches/binary_search.py286-322 - 指数搜索与二分搜索结合

二分法接口函数

该仓库还提供了与 Python 的bisect模块相对应的二分法实用程序

搜索性能基准测试

二分搜索模块包含性能基准测试代码searches/binary_search.py325-351,对不同实现进行比较

来源

排序算法

排序算法在集合中按特定顺序排列元素。该仓库包含基于比较、基于分布和专门的排序实现。

算法性能特征

下表总结了已实现算法的时间和空间复杂度

排序算法

算法最佳情况平均情况最坏情况空间复杂度稳定
冒泡排序O(n)O(n²)O(n²)O(1)
快速排序O(n log n)O(n log n)O(n²)O(log n)
归并排序O(n log n)O(n log n)O(n log n)O(n)
堆排序O(n log n)O(n log n)O(n log n)O(1)
插入排序O(n)O(n²)O(n²)O(1)
选择排序O(n²)O(n²)O(n²)O(1)
Shell SortO(n log n)O(n(log n)²)O(n(log n)²)O(1)
Bucket SortO(n+k)O(n+k)O(n²)O(n+k)
Bead SortO(n)O(n²)O(n²)O(n)
Cocktail Shaker SortO(n)O(n²)O(n²)O(1)
Gnome SortO(n)O(n²)O(n²)O(1)

搜索算法

算法最佳情况平均情况最坏情况空间复杂度先决条件
线性搜索O(1)O(n)O(n)O(1)
递归线性搜索O(1)O(n)O(n)O(n)
二分搜索O(1)O(log n)O(log n)O(1)排序后的集合
二分搜索(递归)O(1)O(log n)O(log n)O(log n)排序后的集合
指数搜索O(1)O(log i)O(log i)O(log n)排序后的集合

来源

常见排序算法实现

冒泡排序

冒泡排序是一种简单的基于比较的算法,它会重复地遍历列表,比较相邻的元素,并在顺序错误时交换它们。该仓库提供了迭代和递归实现。

实现包含两个主要函数

  • bubble_sort_iterative:迭代实现
  • bubble_sort_recursive:递归实现

主要实现细节

  • 迭代版本使用一个标志(swapped),通过在某次遍历中没有发生交换时提前终止来优化性能。
  • 递归版本执行一次完整遍历,并调用自身,直到不再需要交换为止。

来自sorts/bubble_sort.py4-54的示例实现

来源

快速排序

快速排序是一种分治算法,它通过从数组中选择一个“枢轴”元素,并将其他元素根据它们小于或大于枢轴的值划分为两个子数组来工作。

该实现使用随机枢轴选择策略,以避免在已排序数组上的最坏情况性能。quick_sort函数负责分区和递归排序。

来自sorts/quick_sort.py16-43的关键实现细节

  • 使用随机枢轴选择
  • 根据与枢轴的比较创建两个子数组(lessergreater
  • 递归地对子数组进行排序,并将它们与枢轴合并

来源

归并排序

归并排序是一种高效、稳定、基于比较的分治排序算法。它将输入数组分成两半,递归地对它们进行排序,然后合并已排序的两半。

实现包含两个关键函数

  • merge_sort:递归地划分和解决数组
  • merge:合并两个已排序数组的辅助函数

来自sorts/merge_sort.py13-49的关键实现细节

  • 所有情况下的时间复杂度为 O(n log n)
  • 为合并操作使用额外的空间
  • 稳定的排序算法(保留相等元素的相对顺序)

来源

堆排序

堆排序是一种基于比较的排序算法,它使用二叉堆数据结构。它将输入分成已排序区域和未排序区域,并通过提取最大元素并将其插入已排序区域来逐步缩小未排序区域。

实现使用两个主要函数

  • heapify:确保维护堆属性
  • heap_sort:主排序函数,构建堆并提取元素

来自sorts/heap_sort.py6-57的关键实现细节

  • 原地排序算法,辅助空间 O(1)
  • 使用最大堆数据结构
  • 所有情况下的时间复杂度为 O(n log n)

来源

插入排序

插入排序是一种简单的排序算法,它一次构建一个已排序的数组。它对小数据集有效,并且经常被用作更复杂算法的一部分。

sorts/insertion_sort.py27-59中的实现使用了直接方法

  • 从第二个元素(索引 1)开始
  • 对于每个元素,将其与所有前面的元素进行比较
  • 将大于当前元素的元素向右移动一个位置
  • 将当前元素插入正确位置

来源

选择排序

选择排序是一种原地比较排序算法。它将输入列表分为两部分:从左到右构建的已排序子列表和剩余未排序项的子列表。

来自sorts/selection_sort.py1-27的关键实现细节

  • 简单的嵌套循环实现
  • 即使数组已排序,始终执行 O(n²) 次比较
  • 最坏情况下进行 O(n) 次交换
  • 默认不稳定(但可以实现为稳定)

来源

Shell Sort

希尔排序是插入排序的泛化,允许交换相距较远的元素。其思想是安排元素列表,使得从任何位置开始,每隔 h 个元素都能得到一个排序列表。

来自sorts/shell_sort.py6-31的关键实现细节

  • 使用 Marcin Ciura 的间隔序列(经验性得出以获得更好的性能)
  • 通过允许交换相距较远的元素来改进插入排序
  • 时间复杂度取决于使用的间隔序列

来源

Bucket Sort

桶排序是一种分布排序,它通过将数组的元素分布到多个桶中来工作。然后,每个桶都使用不同的排序算法进行单独排序,或通过递归地应用桶排序算法进行排序。

来自sorts/bucket_sort.py34-67的关键实现细节

  • 通过根据值将元素分布到桶中来工作
  • 每个桶使用另一种排序算法(Python 中的 TimSort)进行单独排序
  • 平均时间复杂度为 O(n + k),其中 k 是桶的数量
  • 最坏情况时间复杂度为 O(n²),当所有元素都进入同一个桶时

来源

其他专用排序算法

该仓库还包含几种专用排序算法

  1. 珠子排序 sorts/bead_sort.py - 一种自然排序算法,仅适用于非负整数。

  2. 鸡尾酒排序 sorts/cocktail_shaker_sort.py - 冒泡排序的一种变体,双向排序,在某些情况下可以减少所需传递次数。

  3. 地精排序 sorts/gnome_sort.py - 一种简单的排序算法,类似于插入排序,但一次将元素向后移动一个位置。

  4. 拓扑排序 sorts/topological_sort.py - 一种基于图的排序算法,用于对有向无环图进行排序。

  5. 摆动排序 sorts/wiggle_sort.py - 将数组重新排列成锯齿形模式,其中 nums[0] < nums[1] > nums[2] < nums[3]...

来源

基准测试和使用

许多排序算法的实现都包含基准测试代码和示例。例如,冒泡排序实现比较了迭代版本和递归版本的性能。

要在自己的代码中使用这些排序算法,只需导入所需的函数并将集合传递给它即可。

大多数排序实现遵循相同的接口模式。

  1. 接受集合作为输入
  2. 返回排序后的集合
  3. 提供广泛的 doctest 示例来演示用法。

来源

  • 有关搜索算法的信息,请参阅搜索算法
  • 有关利用排序的数据结构(如堆)的信息,请参阅数据结构