目录
一、选择排序 (Selection Sort)
一种简单的排序算法是这样的:首先,找到数组中最小的元素。其次,将该最小元素与数组中的第一个元素交换位置 (如果第一个元素就是最小元素那么它就和自己交换)。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此循环往复,直到将整个数组排序。这种算法被称为 选择排序,因为它 不断地选择剩余元素之中的最小者。
选择排序是一种简单直观的排序算法,对任意数据都具有 O(n²) 的时间复杂度。通过证明易知,对于长度为 N 的数组,选择排序需要大约 N²/2 次比较和 N 次交换。故使用选择排序时,数据规模越小越好。选择排序的唯一好处是 不占用额外的内存空间 (in-place 操作具有 O(1) 的空间复杂度)。
1.1 原理
步骤 (以从小到大的顺序排列为例):
- 首先在 未排序序列 中查找最小 (大) 元素,与首个元素交换位置,作为 已排序序列 的起始元素。
- 在其余 未排序序列 中查找最小 (大) 元素,与 已排序序列 后一个元素交换位置。
- 重复步骤 2,直到所有 未排序序列 均成为 已排序序列。
演示 (以从小到大的顺序排列为例):
其中,黄色 的是 已排序序列,蓝色 的是 未排序序列,红色 的是 本轮最小元素,绿色 的是 顺序遍历待比较元素。
1.2 实现
1.2.1 基本
def selection_sort(our_list):
for i in range(len(our_list)-1): # 遍历查找总次数 n-1
min_index = i # 将未排序序列的首个元素作为临时最小元素
for j in range(i+1, len(our_list)): # 本轮在未排序序列中的比较次数 n-i-1
if our_list[min_index] > our_list[j]: # 比较得到最小元素 index
min_index = j
if min_index != i: # 如果需要交换
our_list[i], our_list[min_index] = our_list[min_index], our_list[i] # 交换
return our_list
# -------------------------------------- 测试 ---------------------------------------- #
if __name__ == "__main__":
my_list = [7, 3, 5, 6, 9, 2]
print(selection_sort(my_list)) # [2, 3, 5, 6, 7, 9]
1.2.2 优化
双向选择排序:通过交替查找最小值和最大值,并分别进行 正向排序 (从小到大) 和 逆向排序 (从大到小),实现两端已排序序列向中央的逼近,从而有助于减少循环次数。
def selection_sort(our_list):
n = len(our_list)
for i in range(n//2):
# 最小值/最大值索引
min_mark = i
max_mark = n - 1 - i
# 正序排列查找最小值
for j in range(i+1, n-i):
if our_list[j] < our_list[min_mark]:
min_mark = j
if min_mark != i:
our_list[min_mark], our_list[i] = our_list[i], our_list[min_mark]
# 逆序排列查找最大值
for k in range(n-2-i, i, -1):
if our_list[k] > our_list[max_mark]:
max_mark = k
if max_mark != n - 1 - i:
our_list[max_mark], our_list[n-1-i] = our_list[n-1-i], our_list[max_mark]
return our_list
# -------------------------------------- 测试 ---------------------------------------- #
if __name__ == "__main__":
my_list = [7, 3, 5, 6, 9, 2]
print(selection_sort(my_list)) # [2, 3, 5, 6, 7, 9]
1.3 区别
1.3.1 与冒泡排序
选择排序和冒泡排序不同,冒泡排序是比较紧紧相邻的两个元素,而选择排序却不是每次比较紧邻的两个元素。以下代码就不是每次比较紧邻的两个元素,正是选择排序的基本实现。
冒泡排序举例:
选择排序举例:
1.4 小结
其实,选择排序是一种易于理解和实现的简单排序算法,它有两个 特点:
运行时间和输入无关。为找出最小元素而扫描一遍数组并不能为下一遍扫描提供什么有价值的信息。这种性质在某些情况下是缺点。例如,对于一个已经有序的数组或元素全部相等的数组,对其进行选择排序的时间和对一个元素随机乱序排列的数组的排序时间一样长!事实上,其他算法会更善于利用输入的初始状态。
数据移动次数是最少的。每次交换都会改变两个数组元素的值,因此选择排序用了 N 次交换 —— 交换次数和数组大小是线性关系。常见的其他排序算法都不具备这个特征 (大部分的增长数量级都是线性对数或是平方级别)。
总之,选择排序是一种 不稳定 的排序,虽然代码进行了优化,但 平均时间复杂度始终为 O(n²)。此外,还有另一种版本的选择排序选择排序的 堆排序 (要有树基础),时间复杂度可优化至 O(nlogn)。
参考文献: