排序算法:最慢到最快2025年3月17日 | 阅读42分钟 在接下来的教程中,我们将讨论不同的排序算法,并根据它们的复杂度进行比较。 那么,让我们开始吧。 什么是排序算法?排序算法是一组指令,它接受数组或列表作为输入,并将数据元素按特定顺序排列。 排序最常见的是按数字顺序或字母(或词典)顺序进行,排序顺序可以是升序(0-9,A-Z)或降序(9-0,Z-A)。 理解排序算法的重要性排序算法在计算机科学中起着重要作用,因为它们通常可以降低问题的复杂度。这些算法直接应用于搜索算法、数据库管理算法、数据结构算法、分而治之方法等。 以下是一些排序算法在现实世界中实现的最佳示例:
理解数据结构中的排序类型数据结构中的排序技术分为几类:
同时,非原地排序技术利用辅助数据结构来排序原始数组。原地排序技术的一些示例如冒泡排序和选择排序。非原地排序算法的一些示例如归并排序和快速排序。 一些常见的排序算法以下是一些常用排序算法的列表:
让我们通过它们的时间和空间复杂度来检查上述排序算法,以确定最快的排序算法。 冒泡排序冒泡排序是最简单的排序算法。冒泡排序的基本概念是,如果数据元素不处于期望的顺序,则反复交换相邻的数据元素。 假设给定一个包含五个不同数据元素的未排序数组。在这种情况下,冒泡排序将首先比较数组的第一个数据元素和第二个数据元素,如果第一个大于第二个,则立即交换它们,然后继续比较第二个和第三个数据元素,依此类推。 让我们通过一个例子来理解冒泡排序的直观方法: 示例 假设我们有一个包含五个不同数据元素的未排序数组。
我们现在将使用冒泡排序以升序排列这些数组数据元素。 第一次迭代
冒泡排序比较前两个数据元素,并进行交换,因为 5 大于 3 (5 > 3)。
第二次迭代
第三次迭代
第四次迭代
现在我们已经理解了冒泡排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的冒泡排序实现输出 Unsorted Array : 5 3 4 2 1 Sorted Array : 1 2 3 4 5 说明 在上面的代码片段中,我们包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 BubbleSort() 的 void 函数,它接受两个参数——数组名及其大小。在此函数内部,我们定义了两个可迭代对象 i 和 j。我们使用嵌套的 for 循环遍历数组中的每个元素,比较当前元素的值与下一个元素的值,如果当前元素较大,则交换它们的位置。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名 arr 及其大小来调用 BubbleSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (5, 3, 4, 2, 1))被排序为升序(1, 2, 3, 4, 5)。 冒泡排序的性能分析 现在让我们观察冒泡排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察冒泡排序的空间复杂度。 时间复杂度
辅助空间复杂度: O(1) 冒泡排序的一些应用 以下是冒泡排序的一些应用:
选择排序选择排序是一种排序算法,其中给定的数组被分成两个子数组:
最初,已排序的段为空,而未排序的段包含整个列表。在每次迭代中,我们将从未排序的子数组中获取最小的数据元素,并将其推到已排序的子数组的末尾。因此,我们可以成功构建我们的已排序数组。 让我们通过一个例子来理解选择排序的直观方法: 示例 假设我们有一个包含五个不同数据元素的未排序数组。
我们现在将使用选择排序以升序排列这些数组数据元素。
因此,我们将得到以下数组: (16, 26, 23, 28, 18)
因此,我们现在将得到以下数组: (16, 18, 26, 23, 28)
因此,我们现在将得到以下数组: (16, 18, 23, 26, 28)
因此,我们现在将得到以下数组: (16, 18, 23, 26, 28)
因此,最终的已排序数组将是: (16, 18, 23, 26, 28) 现在我们已经理解了选择排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的选择排序实现输出 Unsorted Array : 25 23 28 16 18 Sorted Array : 16 18 23 25 28 说明 在上面的代码片段中,我们包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 SelectionSort() 的 void 函数,它接受两个参数——数组名及其大小。在此函数内部,我们定义了两个可迭代对象 i 和 j 以及一个名为 curr 的变量。我们使用了嵌套的 for 循环来遍历数组中的每个元素。在第一个 for 循环内部,我们将 curr 变量初始化为索引 i 的当前值。在第二个 for 循环内部,我们将索引 j 处的元素值与 curr 位置处的元素进行比较,如果较大,则调用 swap() 函数交换它们的位置。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名 arr 及其大小来调用 SelectionSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (25, 23, 28, 16, 18))被排序为升序(16, 18, 23, 25, 28)。 选择排序的性能分析现在让我们观察选择排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察选择排序的空间复杂度。 时间复杂度
辅助空间复杂度: O(1) 选择排序的一些应用 以下是选择排序的一些应用:
插入排序插入排序是一种排序算法,其中给定的数组被分成已排序和未排序的段。在每次迭代中,我们需要在已排序的子序列中找到要插入的数据元素的最佳位置,然后将其插入,同时将剩余的数据元素向右移动。 让我们通过一个例子来理解插入排序的直观方法: 示例 假设我们有一个包含五个不同数据元素的未排序数组。
我们现在将使用插入排序以升序排列这些数组数据元素。
因此,我们将得到以下数组: (23, 26, 28, 16, 18)
因此,我们的数组将保持不变。 (23, 26, 28, 16, 18)
因此,我们将得到以下数组: (16, 23, 26, 28, 18)
因此,最终的已排序数组将是: (16, 18, 23, 26, 28) 现在我们已经理解了插入排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的插入排序实现输出 Unsorted Array : 25 23 28 16 18 Sorted Array : 16 18 23 25 28 说明 我们在上面的代码片段中包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 InsertionSort() 的 void 函数,它接受两个参数——数组名及其大小。在此函数内部,我们定义了两个可迭代对象 i 和 j 以及一个名为 temp 的变量。我们使用 for 循环遍历数组中的每个元素。在此之内,我们将 temp 变量初始化为 arr[i] 的值,并将可迭代对象 j 初始化为 i 的前一个值。然后我们使用 while 循环来比较索引 j 处的元素值和 temp,如果较大则交换它们的位置。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名 arr 及其大小来调用 InsertionSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (25, 23, 28, 16, 18))被排序为升序(16, 18, 23, 25, 28)。 插入排序的性能分析 现在让我们观察插入排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察插入排序的空间复杂度。 时间复杂度
辅助空间复杂度: O(1) 插入排序的一些应用 以下是插入排序的一些应用:
快速排序快速排序是一种基于分而治之方法的排序算法。此排序技术的直观概念是,它选择一个数据元素作为给定数据元素数组的枢轴,然后围绕该枢轴数据元素对数组进行分区。因此,它递归地调用自身,然后对两个子数组进行分区。 让我们通过一个例子来理解快速排序的直观方法: 示例 ![]() 快速排序算法包含的逻辑步骤如下:
现在我们已经理解了快速排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的快速排序实现输出 Unsorted Array : 25 23 28 16 18 Sorted Array : 16 18 23 25 28 说明 我们在上面的代码片段中包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 partition_array() 的函数来围绕枢轴元素对数组进行分区。此函数接受三个参数——数组名、起始点和终点。在此函数内部,我们初始化了枢轴元素的值并定义了一个可迭代对象进行迭代。然后我们使用 for 循环遍历元素并交换元素以对数组进行排序。然后我们定义了一个名为 QuickSort() 的 void 函数,它接受三个参数——数组名、起始点和终点。在此函数内部,我们比较起始点是否小于终点,并调用 partition_array() 函数。然后我们递归调用 QuickSort() 函数。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名以及起始点和终点来调用 QuickSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (25, 23, 28, 16, 18))被排序为升序(16, 18, 23, 25, 28)。 快速排序的性能分析 现在让我们观察快速排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察快速排序的空间复杂度。 时间复杂度
辅助空间复杂度:O(log n) 快速排序的一些应用 以下是快速排序的一些应用:
合并排序归并排序是另一种基于分而治之方法的排序算法。在每次迭代中,归并排序将输入数组分成两个相等的子数组,递归地为这两个子数组调用自身,最后合并两个已排序的半部分。 让我们通过一个例子来理解归并排序的直观方法: ![]() 现在我们已经理解了归并排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的归并排序实现输出 Unsorted Array : 25 23 28 16 18 Sorted Array : 16 18 23 25 28 说明 我们在上面的代码片段中包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 merge_subarray() 的函数,它接受多个参数,如数组名、起始点、中间点和终点。在此函数内部,我们定义了一些变量以及一些数组。然后我们使用 for 循环将元素插入到这些数组中。然后我们定义并初始化了一些可迭代对象,并使用 while 循环通过交换它们的元素来对子数组进行排序。然后我们定义了另一个名为 MergeSort() 的函数,它接受三个参数——数组名、起始点和终点。在此函数内部, 我们使用 if 条件语句来检查起始点是否大于或等于终点,并添加一个 return 语句来中断循环。然后我们定义并初始化了一个名为 middle 的变量,并递归调用 MergeSort() 函数来对数组进行排序。然后我们调用 merge_subarray() 函数。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名以及起始点和终点来调用 MergeSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (25, 23, 28, 16, 18))被排序为升序(16, 18, 23, 25, 28)。 归并排序的性能分析 现在让我们观察归并排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察归并排序的空间复杂度。 时间复杂度
辅助空间复杂度: O(n) 归并排序的一些应用 以下是归并排序的一些应用:
计数排序计数排序是一种有趣的排序算法,主要侧重于特定范围内唯一数据元素的频率(类似于哈希)。 此排序算法通过计算具有不同键值的 数据元素 的数量,然后在估算每个唯一数据元素在未排序序列中的位置后构建一个排序数组。 计数排序与我们之前讨论的算法不同,因为它不涉及输入数据元素之间的比较。 让我们通过一个例子来理解计数排序的直观方法: 示例 为简单起见,假设我们有一个从 0 到 3 的数据元素组成的未排序数组。
现在处理输入数据:1, 0, 3, 1, 3, 1
最终数组:(0, 1, 1, 1, 3, 3) 现在我们已经理解了计数排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的计数排序实现输出 Unsorted Array : 1 0 3 1 3 1 Sorted Array : 0 1 1 1 3 3 说明 我们在上面的代码片段中包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 getMaximum() 的函数,它接受两个参数——数组名及其大小,以便从数组中检索最大值。在此函数内部,我们首先初始化一个变量并赋予初始最大值。然后我们遍历数组,将变量值更新为最终的最大值,并返回它。然后我们定义了另一个名为 CountingSort() 的函数,它接受两个参数——数组名及其大小。在此函数内部,我们定义了一个用于存储输出的数组,调用了 getMaximum() 函数,以及一个计数器数组。然后我们使用 for 循环将计数器数组的初始值设置为 0。我们再次使用 for 循环来更新给定数组在计数器数组中的值。然后我们再次使用 for 循环遍历计数器数组,修改其值以形成前缀和数组。我们再次使用 for 循环从输入序列生成输出,然后将其计数减 1。最后,我们使用 for 循环遍历数组的数据元素,并根据输出数组修改它们的值。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名以及起始点和终点来调用 CountingSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (1, 0, 3, 1, 3, 1))被排序为升序(0, 1, 1, 1, 3, 3)。 计数排序的性能分析 现在让我们观察计数排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察计数排序的空间复杂度。 时间复杂度
辅助空间复杂度:O(k),其中 k 是数组中唯一元素的计数。 计数排序的一些应用 以下是计数排序的一些应用:
基数排序正如我们已经讨论过的,计数排序之所以与众不同,是因为它不像归并排序或冒泡排序那样是基于比较的排序算法。因此,我们可以将它的时间复杂度降低到线性时间。 然而,当输入数组的数据元素范围从 1 到 n2 时,这种排序技术就会失败,导致其时间复杂度增加到 O(n2)。 基数排序的基本概念是扩展计数排序的功能,以便在输入数组的数据元素范围从 1 到 n2 时获得更好的时间复杂度。 让我们简要了解一下这种排序算法: 算法 对于每个数字 i,其中 i 从数字的最低有效位到最高有效位变化,我们将使用计数排序算法对输入数组进行第 i 位排序。 注意:我们使用了计数排序,因为它是一种稳定的排序算法。让我们通过一个例子来理解基数排序的直观方法: 示例 假设我们有一个包含六个不同数据元素的未排序数组。
我们现在将使用基数排序以升序排列这些数组数据元素。
排序后,我们将得到以下数组: (90, 151, 232, 54, 634, 37)
排序后,我们将得到以下数组: (232, 634, 37, 151, 54, 90)
排序后,我们将得到以下数组: (37, 54, 90, 151, 232, 634),这也是最终的排序数组。 现在我们已经理解了基数排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的基数排序实现输出 Unsorted Array : 54 90 151 37 634 232 Sorted Array : 37 54 90 151 232 634 说明 我们在上面的代码片段中包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 getMaximum() 的函数,它接受两个参数——数组名及其大小,以便从数组中检索最大值。在此函数内部,我们首先初始化一个变量并赋予初始最大值。然后我们遍历数组,将变量值更新为最终的最大值,并返回它。然后我们定义了另一个名为 CountingSort() 的函数,它接受多个参数,如数组名、其大小和有效位。在此函数内部,我们定义了一个用于存储输出的数组,一个计数器数组和一个可迭代对象。然后我们使用多个 for 循环根据选定的有效位对数组进行排序。然后我们定义了一个名为 RadixSort() 的函数,它接受两个参数——数组名及其大小。在此函数内部,我们调用了 getMaximum() 函数,并使用 for 循环调用 CountingSort() 函数。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名及其大小来调用 RadixSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (54, 90, 151, 37, 634, 232))被排序为升序(37, 54, 90, 151, 232, 634)。 基数排序的性能分析 现在让我们观察基数排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察基数排序的空间复杂度。 时间复杂度
辅助空间复杂度:O(n + k),其中 n 是数组中的元素数量,k 是数组中唯一元素的计数。 基数排序的一些应用 以下是基数排序的一些应用:
桶排序桶排序是一种基于比较的排序算法,它将数组的数据元素递归地分成多个桶,然后使用不同的排序算法对这些桶进行单独排序。最后,将已排序的桶重新组合以生成排序后的数组。 让我们借助伪代码来理解桶排序算法: 我们可以通过假设已经创建了一个包含多个“桶”(列表)的数组来进一步探索桶排序算法的工作原理。数据元素现在根据它们的属性从未排序的数组插入到这些“桶”中。这些桶最终使用插入排序算法(如前所述)进行单独排序。 以下是桶排序算法的伪代码: 伪代码 现在我们已经理解了桶排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的桶排序实现输出 Unsorted Array : 0.454 0.583 0.4471 0.6146 0.563 0.9412 Sorted Array : 0.4471 0.454 0.563 0.583 0.6146 0.9412 说明 我们在上面的代码片段中包含了 'bits/stdc++.h' 头文件,定义了一个常量来执行 push back 并使用了标准命名空间。然后我们定义了一个名为 BucketSort() 的函数,它接受两个参数——数组名及其大小,以便对数组的元素进行排序。在此函数内部,我们首先使用 vector 定义了一个数组来表示桶。然后我们使用 for 循环遍历数组,将数据元素从数组推送到桶中。然后我们再次使用 for 循环遍历桶中的元素,并使用插入排序以升序对它们进行排序。然后我们再次使用 for 循环将排序后的元素从桶更新回数组。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名及其大小来调用 BucketSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (0.454 0.583 0.4471 0.6146 0.563 0.9412))被排序为升序(0.4471 0.454 0.563 0.583 0.6146 0.9412)。 桶排序的性能分析 现在让我们观察桶排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察桶排序的空间复杂度。 时间复杂度
辅助空间复杂度:O(n),其中 n 是数组中的元素数量,k 是数组中唯一元素的计数。 桶排序的一些应用 以下是桶排序的一些应用:
梳排序梳排序(Comb Sort)非常有趣。它是冒泡排序算法的一个改进版本。正如有些人之前观察到的,冒泡排序算法在每次迭代中都会比较相邻的数据元素。 然而,梳排序算法通过一个大的间隔值来比较和交换数据元素。间隔值在每次迭代中按 1.3 的因子缩小,直到间隔值达到 1,此时它停止执行。 该收缩因子通过试错法(在测试了超过 20 万个随机数组的梳排序算法后)计算得出为 1.3。 让我们通过一个例子来理解梳排序的直观方法: 示例 假设我们有一个包含十个不同数据元素的未排序数组。
初始间隔值:10
因此,元素将按以下格式排序: 100, 90, 80, 70, 60, 50, 40, 30, 20, 10 30, 90, 80, 70, 60, 50, 40, 100, 20, 10 30, 20, 80, 70, 60, 50, 40, 100, 90, 10 30, 20, 10, 70, 60, 50, 40, 100, 90, 80
由于间隔值为 5 时,数据元素已按升序排列。因此,数组将保持不变,如下所示: 30, 20, 10, 70, 60, 50, 40, 100, 90, 80
因此,元素将按以下格式排序: 30, 20, 10, 70, 60, 50, 40, 100, 90, 80 30, 20, 10, 40, 60, 50, 70, 100, 90, 80
因此,元素将按以下格式排序: 30, 20, 10, 40, 60, 50, 70, 100, 90, 80 10, 20, 30, 40, 60, 50, 70, 100, 90, 80 10, 20, 30, 40, 60, 50, 70, 80, 90, 100
因此,元素将按以下格式排序: 10, 20, 30, 40, 60, 50, 70, 80, 90, 100 10, 20, 30, 40, 50, 60, 70, 80, 90, 100
现在我们已经理解了梳排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的梳排序实现输出 Unsorted Array : 100 90 80 70 60 50 40 30 20 10 Sorted Array : 10 20 30 40 50 60 70 80 90 100 说明 我们在上面的代码片段中包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 CombSort() 的函数,它接受两个参数——数组名及其大小,以便对数组的元素进行排序。在此函数内部,我们首先定义了指定间隔值的变量,并将一个布尔标志设置为 True。然后我们使用 while 循环,该循环将一直迭代直到间隔值减小到 1 并且标志变为 False。我们通过将当前间隔除以 1.3 来更新间隔值。我们还使用了 if 条件语句来检查间隔值是否小于 1 并将其设置为 1。我们还更新了标志的值,并使用 for 循环遍历数组中的数据元素并根据它们的间隔值进行排序。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名及其大小来调用 CombSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (100, 90, 80, 70, 60, 50, 40, 30, 20, 10))被排序为升序(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)。 梳排序的性能分析 现在让我们观察梳排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察梳排序的空间复杂度。 时间复杂度
辅助空间复杂度: O(1) 梳排序的一些应用 以下是梳排序的一些应用:
希尔排序希尔排序算法是插入排序算法的一个改进版本,在该版本中,我们采用递减分区来对数据进行排序。 在每次传递中,对于数组的每次传递,间隔大小都减半。因此,对于每次迭代,数组的数据元素都会与计算出的间隔值进行比较(如果需要)并交换。 让我们借助伪代码来理解希尔排序算法: 以下是希尔排序算法的伪代码: 伪代码 希尔排序的概念允许交换相距很远的数据元素。在希尔排序中,我们将数组 N 排序为较大的 N 值。 然后我们不断减小 N 的值,直到它变为 1。如果数组中每 N 个元素的子数组都已排序,则该数组称为 N 排序。 对于每次迭代,间隔大小将是 floor(N/2)。 现在我们已经理解了希尔排序的直观概念,是时候考虑它在 C++ 编程语言中的标准实现了。 C++ 中的希尔排序实现输出 Unsorted Array : 100 90 80 70 60 50 40 30 20 10 Sorted Array : 10 20 30 40 50 60 70 80 90 100 说明 我们在上面的代码片段中包含了 'bits/stdc++.h' 头文件并使用了标准命名空间。然后我们定义了一个名为 ShellSort() 的函数,它接受两个参数——数组名及其大小,以便对数组的元素进行排序。在此函数内部,我们使用了嵌套的 for 循环来根据间隔值遍历数组的元素。然后我们定义并初始化了变量作为当前数组元素以及一个可迭代对象。然后我们再次使用 for 循环以升序对数组进行排序。 现在在 main 函数内部,我们定义并初始化了一个未排序的数组 arr。然后我们计算了它的大小。我们还为用户打印了未排序数组的元素。然后我们通过传递数组名及其大小来调用 ShellSort() 函数。最后,我们为用户打印了排序后的数组元素。 结果,给定的数组(即 (100, 90, 80, 70, 60, 50, 40, 30, 20, 10))被排序为升序(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)。 希尔排序的性能分析 现在让我们观察希尔排序在最佳情况、平均情况和最坏情况下的时间复杂度。我们还将观察希尔排序的空间复杂度。 时间复杂度
辅助空间复杂度: O(1) 希尔排序的一些应用 以下是希尔排序的一些应用:
排序算法的完整性能分析图以下是我们之前研究过的不同排序算法以及一些其他算法的完整性能分析图: 复杂度比较表
什么是最好的排序算法?从上表可以看出,快速排序在最佳和平均情况下的时间复杂度为 O(n log n),在最坏情况下的时间复杂度为 O(n2)。 然而,由于快速排序在大多数输入的平均情况下占有优势,因此该算法通常被认为是“最快”的排序算法。 最简单的排序算法是什么?冒泡排序被广泛认为是所有排序算法中最简单的。该算法的基本概念是扫描整个数组,比较相邻的数据元素并交换它们(如果需要),直到数组被排序。 对于几乎排序的列表,会优先选择哪种排序算法?当我们必须对几乎排序的列表进行排序时,插入排序算法是明确的选择,特别是因为其时间复杂度在这种样本上从惊人的 O(n2) 降低到 O(n)。 与插入排序相比,归并排序和快速排序等算法通常不适合几乎排序的数组。 下一个主题扩展欧几里得算法 |
我们请求您订阅我们的新闻通讯以获取最新更新。