尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

八大排序算法深度解析:从原理到实战选型指南

八大排序算法深度解析:从原理到实战选型指南 1. 排序算法程序员的“内功”与效率基石在编程世界里排序算法就像厨师的刀工、木匠的刨子是每个开发者绕不开的基本功。无论你是处理海量用户数据还是优化一个简单的列表展示排序的效率直接决定了程序的响应速度和资源消耗。很多人觉得排序算法是教科书里的老古董面试时背一背就完事了但真正在项目中遇到性能瓶颈时你才会发现对排序算法原理的深刻理解是写出高效、优雅代码的关键。今天我们不谈空泛的理论就从一个一线开发者的视角把这八种最常见、最实用的排序算法掰开揉碎了讲清楚从最直观的“冒泡”到高效的“快排”再到稳定的“归并”我会结合实际的场景、代码细节和那些容易踩的坑让你不仅“知道”更能“掌握”和“用好”。2. 排序算法全景图从理解到选型在深入每个算法之前我们必须建立一个全局视角。排序算法种类繁多但核心的评价维度就那几个时间复杂度、空间复杂度、稳定性和适用场景。时间复杂度衡量的是算法执行时间随数据量增长的趋势空间复杂度衡量的是算法运行所需额外内存的大小。稳定性则是指如果待排序序列中存在两个相等的元素排序后它们的相对次序是否保持不变。这个特性在某些场景下至关重要比如先按成绩排序再按学号排序稳定的排序能保证相同成绩的学生依然按学号有序。2.1 算法分类与核心特性对比根据排序过程中数据元素是否完全在内存中可分为内部排序数据全部在内存和外部排序数据量太大需分批调入内存。我们今天讨论的八种都属于内部排序。根据主要操作又可大致分为以下几类比较类排序通过比较元素间的大小来决定次序。其平均时间复杂度下限是 O(n log n)代表算法有快速排序、归并排序、堆排序。非比较类排序不通过比较而是利用数据的特定属性如整数范围来确定次序。可以在线性时间 O(n) 内完成但对数据有特殊要求如计数排序、桶排序、基数排序。为了让你一目了然我把这八种算法的核心特性做成了下面这个表格这是后续选型的重要依据排序算法平均时间复杂度最坏时间复杂度空间复杂度是否稳定核心思想冒泡排序O(n²)O(n²)O(1)稳定相邻元素比较交换每一轮将最大/小元素“冒泡”到顶端。选择排序O(n²)O(n²)O(1)不稳定每轮从未排序部分选出最小大元素放到已排序序列末尾。插入排序O(n²)O(n²)O(1)稳定将未排序元素逐个插入到已排序序列的合适位置。希尔排序O(n log n) ~ O(n²)O(n²)O(1)不稳定改进的插入排序通过增量分组进行预处理减少移动次数。归并排序O(n log n)O(n log n)O(n)稳定分治法。将序列递归分成两半分别排序再合并两个有序子序列。快速排序O(n log n)O(n²)O(log n) ~ O(n)不稳定分治法。选取一个基准将序列分成小于基准和大于基准的两部分递归排序。堆排序O(n log n)O(n log n)O(1)不稳定利用堆一种完全二叉树数据结构反复构建大顶堆并交换堆顶元素。计数排序O(n k)O(n k)O(n k)稳定非比较排序。统计每个元素出现的次数然后按统计结果输出。注意表格中的 k 对于计数排序和桶排序通常表示数据的范围最大值与最小值的差加一。快速排序的空间复杂度取决于递归深度平均为 O(log n)最坏如已排序序列为 O(n)。2.2 如何根据场景选择排序算法没有最好的算法只有最合适的算法。选择时你需要问自己几个问题数据规模有多大小规模数据如 n 50O(n²) 的简单排序如插入排序可能因为常数项小反而更快。大规模数据必须选择 O(n log n) 的算法。数据是否基本有序对于近乎有序的序列插入排序和冒泡排序可以接近 O(n)而快速排序可能退化成 O(n²)。是否需要稳定排序如果需要保持相等元素的原始相对顺序则归并排序、计数排序是稳定选择快速排序和堆排序则不行。内存空间是否紧张堆排序和希尔排序是原地排序空间复杂度 O(1)而归并排序和计数排序需要额外的 O(n) 或更多空间。数据是否有特殊性质如果数据是有限范围内的整数计数排序或桶排序的线性时间复杂度是降维打击。举个例子在 Java 中对基本类型的数组排序 (Arrays.sort())早期版本使用了改进的快速排序Dual-Pivot Quicksort因为对基本类型稳定性不重要且追求平均速度。而对于对象数组则使用 TimSort一种归并排序和插入排序的混合体因为对象排序通常需要稳定性。3. 八大排序算法深度解析与实战接下来我们进入正题逐一拆解这八种算法。我会用通俗的语言解释原理给出清晰的代码示例以 Python 为例因其语法清晰并附上关键的注意事项和性能分析。3.1 冒泡排序最直观的入门算法冒泡排序是许多人学到的第一个排序算法。它的思想就像水中的气泡较大的元素会逐渐“浮”到序列的顶端末尾。算法重复地遍历待排序序列一次比较两个相邻元素如果它们的顺序错误就把它们交换过来。遍历序列的工作重复进行直到没有再需要交换的元素此时序列排序完成。核心步骤比较相邻元素。如果第一个比第二个大升序就交换它们。对每一对相邻元素做同样的工作从开始第一对到结尾的最后一对。这步做完后最后的元素会是最大的数。针对所有的元素重复以上的步骤除了最后一个已排序部分。重复步骤1~3直到排序完成。Python 实现def bubble_sort(arr): n len(arr) # 外层循环控制排序的轮数共需 n-1 轮 for i in range(n - 1): # 优化标志位如果某一轮没有发生交换说明已完全有序可提前结束 swapped False # 内层循环进行相邻比较每轮结束后末尾 i 个元素已有序 for j in range(0, n - 1 - i): if arr[j] arr[j 1]: arr[j], arr[j 1] arr[j 1], arr[j] swapped True if not swapped: break return arr实操心得与注意事项优化点上述代码加入了swapped标志位进行优化。对于已经有序或接近有序的序列可以在第一轮遍历后就提前终止将最好情况时间复杂度优化到 O(n)。为什么是 O(n²) 两层嵌套循环最坏情况下需要比较 (n-1) (n-2) ... 1 n(n-1)/2 次故为 O(n²)。稳定性因为只有当前者大于后者时才交换等于时不交换所以相等元素的相对位置不会改变是稳定的。使用场景几乎仅用于教学因其效率低下。在实际工程中除非数据量极小10且你确定数据基本有序否则不应使用。3.2 选择排序简单粗暴的“打擂台”选择排序的思路非常直观在未排序序列中找到最小大元素存放到排序序列的起始位置然后再从剩余未排序元素中继续寻找最小大元素然后放到已排序序列的末尾。以此类推直到所有元素均排序完毕。你可以把它想象成“打擂台”每一轮选出一个冠军放到前面。核心步骤初始状态整个序列为无序区。第 i 轮i 从 0 开始在无序区arr[i...n-1]中选出最小的元素。将该最小元素与无序区的第一个元素arr[i]交换。此时arr[0...i]构成有序区。重复步骤2-3共进行 n-1 轮。Python 实现def selection_sort(arr): n len(arr) for i in range(n - 1): # 假设当前索引 i 的元素是最小值 min_idx i # 在 i1 到 n-1 的范围内寻找真正的最小值索引 for j in range(i 1, n): if arr[j] arr[min_idx]: min_idx j # 将找到的最小元素与第 i 个元素交换 arr[i], arr[min_idx] arr[min_idx], arr[i] return arr实操心得与注意事项不稳定性示例序列[5, 8, 5, 2, 9]。第一轮找到最小元素 2与第一个5交换序列变为[2, 8, 5, 5, 9]。原来在前面的5索引0被交换到了后面索引2破坏了与另一个5的相对顺序。因此选择排序是不稳定的。与冒泡排序的区别冒泡排序通过相邻交换逐步将大元素“推”到后面每轮可能进行多次交换。选择排序每轮只进行一次交换将最小元素“放置”到正确位置。交换次数更少但比较次数依然是 O(n²)。使用场景同样效率低下但因其交换次数少在数据移动成本非常高的场景下比如排序的数据是存储在外部设备上的大型记录可能有一点点优势。但绝大多数情况下不推荐使用。3.3 插入排序扑克牌理牌法插入排序的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列对于未排序数据在已排序序列中从后向前扫描找到相应位置并插入。这就像我们打扑克牌时一张张抓牌并将新抓的牌插入到手中有序牌的正确位置。核心步骤将第一个元素视为已排序序列。取出下一个元素在已排序序列中从后向前扫描。如果该元素已排序大于新元素将该元素移到下一位置。重复步骤3直到找到已排序的元素小于或等于新元素的位置。将新元素插入到该位置后。重复步骤2~5。Python 实现def insertion_sort(arr): n len(arr) # 从第二个元素开始索引1视为待插入元素 for i in range(1, n): key arr[i] # 当前待插入的元素 j i - 1 # 已排序序列的末尾索引 # 从后向前扫描已排序序列寻找 key 的插入位置 while j 0 and key arr[j]: arr[j 1] arr[j] # 将大于 key 的元素向后移动一位 j - 1 arr[j 1] key # 将 key 插入到找到的位置 return arr实操心得与注意事项近乎有序序列的王者当待排序序列基本有序时插入排序的内层while循环很快会结束时间复杂度接近 O(n)。这是它最大的优势。稳定性在寻找插入位置时遇到相等的元素会停止移动key arr[j]才移动因此相等元素的相对顺序不变是稳定排序。小规模数据优选由于实现简单且对于小规模或局部有序数据效率很高它常被用作高级排序算法如快速排序、归并排序在递归到小规模子问题时的优化手段例如当子数组长度小于某个阈值如10时改用插入排序。移动操作多如果原始序列是逆序的每个元素都需要移动前面所有的有序元素导致最坏情况 O(n²)。3.4 希尔排序插入排序的威力增强版希尔排序是插入排序的一种更高效的改进版本也称为缩小增量排序。它通过将原始序列分割成若干个子序列由增量间隔决定分别对这些子序列进行插入排序。随着增量逐渐减小子序列越来越长也越来越有序。当增量减至1时整个序列被当作一个子序列进行最后一次插入排序此时因为序列已经“基本有序”所以插入排序效率很高。核心步骤使用希尔增量序列n/2, n/4, ..., 1选择一个增量序列gap通常初始值为n/2。按增量gap将序列分成gap个子序列对每个子序列进行插入排序。缩小增量gap如gap gap // 2重复步骤2。当gap减少到 1 时对整个序列进行一次插入排序排序完成。Python 实现def shell_sort(arr): n len(arr) gap n // 2 # 初始增量 while gap 0: # 从第 gap 个元素开始对其所在子序列进行插入排序 for i in range(gap, n): temp arr[i] j i # 对子序列进行插入排序 while j gap and arr[j - gap] temp: arr[j] arr[j - gap] j - gap arr[j] temp gap // 2 # 缩小增量 return arr实操心得与注意事项增量序列是关键希尔排序的性能高度依赖于增量序列的选择。上述代码使用的是希尔原始序列n/2^k其最坏情况时间复杂度仍是 O(n²)。更优的序列如 Hibbard 序列、Sedgewick 序列等可以将最坏情况复杂度提升到 O(n^(3/2)) 甚至 O(n log² n)。不稳定性的来源由于元素是跳跃式移动的间隔gap相等的元素可能在分属不同子序列的排序过程中被打乱相对顺序因此是不稳定的。为何高效早期的增量较大子序列元素少排序快后期的增量小子序列基本有序插入排序效率高。它突破了简单插入排序只能相邻移动的局限使得元素可以大步移动更快地到达其最终位置附近。使用场景希尔排序是第一个突破 O(n²) 的排序算法实现简单不需要额外的内存空间原地排序。适用于中等规模的数据排序在一些嵌入式系统或内存受限的环境中仍有应用。3.5 归并排序稳定高效的分治典范归并排序是建立在归并操作上的一种有效的排序算法该算法是采用分治法的一个非常典型的应用。它将已有序的子序列合并得到完全有序的序列。即先使每个子序列有序再使子序列段间有序。核心步骤递归版分解将长度为 n 的序列分成两个长度为 n/2 的子序列。解决对这两个子序列分别递归地应用归并排序。合并将两个已排序的子序列合并成一个有序序列。合并操作是归并排序的核心需要额外的空间。Python 实现def merge_sort(arr): if len(arr) 1: return arr # 1. 分解 mid len(arr) // 2 left arr[:mid] right arr[mid:] # 2. 解决递归排序 left merge_sort(left) right merge_sort(right) # 3. 合并 return merge(left, right) def merge(left, right): result [] i j 0 # 比较两个子序列的头部将较小的放入结果 while i len(left) and j len(right): if left[i] right[j]: # 注意这里是 保证了稳定性 result.append(left[i]) i 1 else: result.append(right[j]) j 1 # 将某个子序列剩余的部分全部追加到结果末尾 result.extend(left[i:]) result.extend(right[j:]) return result实操心得与注意事项稳定性合并操作中当左子序列元素右子序列元素时优先取左子序列的元素。这保证了相等元素的原始相对顺序因此归并排序是稳定的。空间复杂度 O(n)合并过程需要与原始数组等长的额外空间来存储中间结果。这是其主要的缺点。虽然有原地归并的变种但实现复杂且性能通常不如需要额外空间的版本。时间复杂度稳定 O(n log n)无论数据初始状态如何归并排序的时间复杂度都是 O(n log n)。因为它总是均匀地分解序列合并的代价是线性的。这使得它在需要稳定排序且对最坏情况有时间要求的场景下非常可靠。递归与迭代上述是递归实现清晰易懂但有递归栈的开销。也可以使用迭代自底向上的方式实现归并排序避免递归深度问题。使用场景Java 中对象数组的排序 (Arrays.sort()for Object[])、Python 的sorted()函数内部使用的 TimSort 算法其基础就是归并排序。适用于链表排序因为链表随机访问慢但归并排序的合并操作对链表很友好以及需要稳定排序的外部排序数据量大到内存放不下场景。3.6 快速排序平均性能的王者快速排序同样使用分治思想。它从序列中挑出一个元素称为“基准”然后重新排列序列所有比基准值小的元素摆放在基准前面所有比基准值大的元素摆在基准的后面。在这个分区退出之后该基准就处于序列的中间位置。这个过程称为分区操作。然后递归地对基准前后的子序列进行快速排序。核心步骤挑选基准从序列中挑出一个元素作为基准。分区操作重新排列序列形成“左小右大”的格局基准位于最终位置。递归排序递归地将小于基准的子序列和大于基准的子序列排序。Python 实现经典 Lomuto 分区方案def quick_sort(arr, low, high): if low high: # pi 是分区操作后基准元素的正确位置索引 pi partition(arr, low, high) # 递归排序基准左右两边的子序列 quick_sort(arr, low, pi - 1) quick_sort(arr, pi 1, high) def partition(arr, low, high): # 选择最右边的元素作为基准 pivot arr[high] # i 指向小于基准的子序列的末尾 i low - 1 for j in range(low, high): # 如果当前元素小于或等于基准 if arr[j] pivot: i 1 arr[i], arr[j] arr[j], arr[i] # 将基准元素放到正确的位置i1 arr[i 1], arr[high] arr[high], arr[i 1] return i 1 # 调用方式quick_sort(arr, 0, len(arr)-1)实操心得与注意事项基准选择是灵魂上述实现固定选择最右元素作为基准这在序列已排序或逆序时会导致分区极度不平衡每次只能分出一个元素从而使递归树退化成链表时间复杂度恶化到 O(n²)。优化方法随机选择基准random.randint(low, high)或使用三数取中法取头、中、尾元素的中位数。不稳定性分区过程中元素会发生跳跃式交换。例如序列[3, 2, 2, 1]以最后一个1为基准交换可能打乱两个2的顺序。因此快速排序是不稳定的。原地排序标准的快速排序是原地排序空间复杂度主要来自递归调用栈平均为 O(log n)最坏为 O(n)。小数组优化和插入排序一样当递归到的子数组规模很小时如长度 10快速排序的递归开销可能比排序本身还大。此时可以切换到插入排序。使用场景快速排序在平均情况下是内部排序算法中性能最好的常数因子小缓存局部性好。C STL 的sort() Java 对基本类型的Arrays.sort()以及许多语言的标准库排序函数其底层实现都是经过大量优化的快速排序变种如 IntroSort 混合了快速排序、堆排序。3.7 堆排序利用堆结构的智慧堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构并同时满足堆的性质即子节点的键值或索引总是小于或者大于它的父节点。大顶堆用于升序排序小顶堆用于降序排序。核心步骤构建初始堆将待排序序列构造成一个大顶堆。此时整个序列的最大值就是堆顶的根节点。交换与调整将堆顶元素最大值与末尾元素交换此时末尾元素为最大值。然后将剩余 n-1 个元素重新调整成一个大顶堆。重复执行重复步骤2直到堆的大小为1排序完成。Python 实现def heapify(arr, n, i): 调整以 i 为根的子树为大顶堆 largest i # 初始化最大值为根 left 2 * i 1 right 2 * i 2 # 如果左子节点存在且大于根 if left n and arr[left] arr[largest]: largest left # 如果右子节点存在且大于当前最大值 if right n and arr[right] arr[largest]: largest right # 如果最大值不是根则交换并递归调整被破坏的子堆 if largest ! i: arr[i], arr[largest] arr[largest], arr[i] heapify(arr, n, largest) def heap_sort(arr): n len(arr) # 1. 构建大顶堆。从最后一个非叶子节点开始向上调整 for i in range(n // 2 - 1, -1, -1): heapify(arr, n, i) # 2. 逐个交换堆顶元素到末尾并调整堆 for i in range(n - 1, 0, -1): arr[0], arr[i] arr[i], arr[0] # 交换 heapify(arr, i, 0) # 调整剩余 i 个元素的堆 return arr实操心得与注意事项不稳定性在堆调整 (heapify) 的过程中元素的交换是跳跃式的。例如序列[2, 2, 1]构建堆时可能打乱两个2的顺序。因此堆排序是不稳定的。时间复杂度稳定 O(n log n)建堆过程时间复杂度为 O(n)随后进行 n-1 次调整每次调整复杂度为 O(log n)故总复杂度为 O(n log n)。且最好、最坏、平均情况都是如此性能稳定。原地排序堆排序只需要常数级别的额外空间用于交换是原地排序算法。缓存不友好堆排序的数据访问模式是跳跃的沿着二叉树父子节点访问对 CPU 缓存局部性不友好因此其实际运行效率通常不如快速排序和归并排序。使用场景堆排序非常适合在需要同时找到最大值或最小值的场景例如优先级队列的实现。也常用于数据流中实时获取 Top K 元素维护一个大小为 K 的小顶堆。由于其最坏情况也是 O(n log n) 且空间复杂度低在一些对最坏情况有要求的嵌入式系统中有所应用。3.8 计数排序非比较排序的线性时间奇迹计数排序不是基于比较的排序算法其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序计数排序要求输入的数据必须是有确定范围的整数。核心步骤找出极值遍历数组找出待排序序列中的最大值max_val和最小值min_val。创建计数数组创建一个长度为max_val - min_val 1的计数数组count初始化为0。统计频次遍历原数组统计每个元素出现的次数存入count数组对应的位置索引为元素值 - min_val。累加计数为了稳定性将count数组顺序求和使得count[i]表示小于等于i min_val的元素个数。反向填充从原数组末尾开始反向遍历根据count数组确定该元素在输出数组中的最终位置放入结果数组并将对应计数减1。Python 实现def counting_sort(arr): if not arr: return [] # 1. 找出最大值和最小值 min_val, max_val min(arr), max(arr) range_of_elements max_val - min_val 1 # 2. 创建并初始化计数数组 count [0] * range_of_elements output [0] * len(arr) # 3. 统计每个元素的出现次数 for num in arr: count[num - min_val] 1 # 4. 累加计数使 count[i] 包含小于等于 imin_val 的元素个数 for i in range(1, len(count)): count[i] count[i - 1] # 5. 反向填充输出数组以保证稳定性 for num in reversed(arr): # 反向遍历是关键 output[count[num - min_val] - 1] num count[num - min_val] - 1 return output实操心得与注意事项稳定性实现的关键步骤5中反向遍历原数组是保证计数排序稳定性的精髓。这样后出现的相等元素会被放在输出数组靠后的位置保持了它们原有的相对顺序。空间换时间计数排序需要额外的空间来存储计数数组和输出数组空间复杂度为 O(n k)其中 k 是数据的范围。当数据范围k远大于数据量n时例如排序[1, 1000000]空间浪费会非常严重。局限性只能用于整数排序或可映射为整数的数据如字符。对于浮点数或自定义对象需要额外的处理。使用场景适用于数据范围不大且已知的整数排序例如高考分数排序0-750分、年龄排序等。它也是基数排序的基础子程序。4. 算法实战中的常见问题与排查技巧理解了原理和代码在实际应用中还是会遇到各种问题。下面我整理了一些常见坑点和排查思路。4.1 递归深度溢出快速排序与归并排序的陷阱当待排序数据量极大或序列本身已有序对劣质快速排序而言时递归调用深度可能超过系统栈的容量导致RecursionError。问题表现程序运行时报错maximum recursion depth exceeded。排查与解决对于快速排序首要原因是基准选择不当导致递归树不平衡。务必使用随机化基准或三数取中法。其次可以设置递归深度限制当子数组规模小于某个阈值如16时改用非递归的插入排序。对于归并排序可以改用迭代自底向上的版本完全避免递归。或者使用系统调用增加递归深度限制如 Python 的sys.setrecursionlimit()但这只是权宜之计。通用方案考虑使用显式栈来模拟递归过程实现非递归版本的排序算法。4.2 排序稳定性导致的业务逻辑错误这是一个非常隐蔽的 bug。假设你有一份学生成绩单先按语文成绩降序排序再按数学成绩降序排序。如果第二次排序使用的算法不稳定那么语文成绩相同的学生他们的数学成绩排序可能会打乱第一次排序建立的语文成绩顺序。问题表现多条件排序后结果不符合“次要条件相同时主要条件顺序不变”的预期。排查与解决明确需求首先确认业务是否要求排序稳定。选择稳定算法如果需要稳定排序则必须选择归并排序、计数排序、冒泡排序、插入排序等稳定算法。Python 的sorted()和list.sort()是稳定的。Java 中对象数组的Arrays.sort()也是稳定的。避免不稳定算法在需要稳定性的场景下切忌使用快速排序、堆排序、选择排序、希尔排序。测试验证编写单元测试用包含重复元素的序列测试排序函数的稳定性。4.3 特殊数据下的性能悬崖某些算法在特定数据分布下性能会急剧下降。快速排序遇有序序列如果基准选择策略简单如总是选第一个或最后一个对已排序或逆序序列排序会退化成 O(n²)。解决方案随机化基准。插入排序遇逆序序列同样退化成 O(n²)。解决方案对于随机数据小规模再用插入排序对于可能逆序的大数据避免单独使用插入排序。计数排序遇范围过大如果整数范围极大如从1到10^9计数数组将占用巨大内存。解决方案考虑使用桶排序或基数排序进行分段处理。4.4 内存使用超标非原地排序的隐患归并排序和计数排序需要 O(n) 或更多的额外空间。当排序数据量达到内存级别时可能导致内存不足OOM。问题表现程序运行过程中内存占用持续飙升最终被系统终止。排查与解决分析数据量估算待排序数据的总内存占用。选择原地算法如果内存紧张优先选择堆排序、希尔排序或优化后的快速排序原地分区。外部排序如果数据量远超内存例如几个GB的文件则必须使用外部排序算法其思想是归并排序的延伸将数据分块读入内存排序再写回磁盘进行多路归并。4.5 自定义对象排序的实现要点在工程中我们更常排序的是自定义的类对象而非简单整数。Python需要实现__lt__(小于) 魔术方法或者向sorted()或list.sort()传递key函数返回用于比较的键或cmp函数比较函数。# 使用 key students [{name: Alice, score: 90}, ...] sorted_students sorted(students, keylambda x: x[score], reverseTrue) # 实现 __lt__ class Student: def __init__(self, name, score): self.name name self.score score def __lt__(self, other): return self.score other.score # 按分数升序Java需要让类实现Comparable接口并重写compareTo方法或者向Arrays.sort()或Collections.sort()传递一个Comparator对象。C需要重载运算符或为std::sort提供自定义的比较函数/函数对象。关键点确保比较逻辑满足严格弱序即自反性、反对称性和传递性。错误的比较逻辑例如比较函数对相等元素返回true可能导致未定义行为甚至程序崩溃。
返回列表