📅  最后修改于: 2020-09-05 11:17:31             🧑  作者: Mango
排序虽然是一项基本操作,但却是计算机应执行的最重要的操作之一。它是许多其他算法和过程(例如搜索和合并)的基础。了解不同的排序算法可以帮助您更好地理解不同算法背后的思想,并帮助您提出更好的算法。
在选择排序算法排序通过找到未排序部分的最小值,然后与所述第一未排序的元件交换它的阵列。它是就地算法,这意味着您不需要分配其他列表。尽管速度很慢,但在内存有限的系统中,它仍被用作主要的排序算法。
在本文中,我们将解释选择排序的工作原理并在Python中实现它。然后,我们将分解算法的动作,以了解其时间复杂度。
那么选择排序如何工作?选择排序将输入列表分为两部分,已排序的部分最初为空,未排序的部分最初包含所有元素的列表。该算法然后选择所有未排序文件的最小值,并将其与第一个未排序值交换,然后将排序部分增加一个。
此类的高级实现如下所示:
def selection_sort(L):
for i in range(len(L) - 1):
min_index = argmin(L[i:])
L[i], L[min_index] = L[min_index], L[i]
在上面的伪代码中,argmin()
是一个返回最小值索引的函数。该算法使用变量i
来跟踪已排序列表的结束位置和未排序列表的开始位置。由于我们从没有排序的项目开始并取最小值,因此,总是存在未排序部分的每个成员大于已排序部分的任何成员的情况。
第一行增加的值i
,第二行找到最小值的索引,第三行交换这些值。交换之所以有效,是因为Python在将任何内容分配给左侧之前先计算了右侧,因此我们不需要任何临时变量。
让我们来看看它是如何工作的行动,包含以下元素的列表:[3, 5, 1, 2, 4]
。
我们从未排序的列表开始:
未排序的部分包含所有元素。我们仔细检查每个项目,并确定这1
是最小的元素。所以,我们换1
用3
:
在其余未排序的元素中[5, 3, 2, 4]
,2
是最低的数字。现在2
,我们交换5
:
此过程将继续进行,直到对列表进行排序:
让我们看看如何在Python中实现它!
实现此算法的技巧是跟踪最小值并交换列表的两个元素。sort.py
在您最喜欢的编辑器中打开一个名为的文件,然后在其中输入以下代码:
def selection_sort(L):
# i indicates how many items were sorted
for i in range(len(L)-1):
# To find the minimum value of the unsorted segment
# We first assume that the first element is the lowest
min_index = i
# We then use j to loop through the remaining elements
for j in range(i+1, len(L)-1):
# Update the min_index if the element at j is lower than it
if L[j] < L[min_index]:
min_index = j
# After finding the lowest item of the unsorted regions, swap with the first unsorted item
L[i], L[min_index] = L[min_index], L[i]
现在,将一些代码添加到文件中以测试算法:
L = [3, 1, 41, 59, 26, 53, 59]
print(L)
selection_sort(L)
# Let's see the list after we run the Selection Sort
print(L)
然后,您可以打开一个终端并运行以查看结果:
$ python sort.py
[3, 1, 41, 59, 26, 53, 59]
[1, 3, 26, 41, 53, 59, 59]
该列表已正确排序!我们知道它是如何工作的,并且可以在Python中实现选择排序。让我们进入一些理论,看看它在时间方面的表现。
那么,选择排序对列表进行排序需要多长时间?给定一个size数组,我们将采用一种方法并精确计算选择排序算法所花费的时间n
。代码的第一行是:
def selection_sort(L):
这行不应该花费太多,因为它只是设置函数堆栈。我们说这是一个常数 -输入的大小不会改变此代码运行的时间。假设c1
执行此行代码需要执行操作。接下来,我们有:
for i in range(len(L)-1):
这个有点棘手。首先,我们有两个函数调用len()
和range()
,它们在for
循环开始之前执行。len()
CPython 的成本也与CPython的大小无关,CPython是Windows,Linux和Mac上的默认Python实现。的初始化也是如此range()
。让我们把这两个叫做c2
。
接下来,我们有for
,即运行n - 1
时间。这不是一个常数,输入的大小确实会影响执行的时间。因此,我们必须将完成一个循环所需的时间乘以n - 1
。
可以in
说,评估运营商的成本是恒定的c3
。这涵盖了外部for循环。
变量分配也可以在恒定时间内完成。我们称这个为c4
:
min_index = i
现在,我们遇到内部for循环。它具有两个常量函数调用。假设他们采取c5
行动。
请注意,
c5
它与有所不同c2
,因为range
这里有两个参数,并且这里要执行加法运算。
到目前为止,我们已经有了c1 + c2 + (n - 1) * (c3 + c4 + c5)
操作,然后我们的内循环开始,将所有内容乘以…?好吧,这很棘手,但是如果仔细观察,它会n - 2
在第一个循环中花费时间,n - 3
在第二个循环中花费时间,最后一次则花费1。
我们需要将所有内容乘以1到之间的所有数字的总和n - 2
。数学家告诉我们,总和为(n - 2) * (n - 1) / 2
。随时x
在这里阅读有关1和任何正数之间的整数之和的更多信息。
内循环的内容也将在固定时间内完成。让我们把它需要的Python做的时候in
,if
,赋值语句和变量交换占用的任意固定时间c6
。
for j in range(i+1, len(L)-1):
if L[j] < L[min_index]:
min_index = j
L[i], L[min_index] = L[min_index], L[i]
大家一起得到c1 + c2 + (n - 1) * (c3 + c4 + c5) + (n - 2) * (n - 3) * c6 / 2
。
我们可以简化这个到:a * n * n + b * n + c
,其中a
,b
和c
代表评估常量的值。
这称为O(n 2)。那是什么意思?总而言之,我们算法的性能基于输入列表的平方大小。因此,如果我们将列表大小加倍,则将其排序所需的时间将乘以4!如果我们将输入的大小除以3,则时间将减少9!
在本文中,我们研究了选择排序的工作原理并在Python中实现了它。然后,我们逐行分解代码以分析算法的时间复杂度。