【算法1】选择排序 - 插入排序
选择排序法
数据结构 数组
最坏时间复杂度 О(n²)
最优时间复杂度 О(n²)
平均时间复杂度 О(n²)
最坏空间复杂度 总共О(n),需要辅助空间O(1)
文字描述:从待排序的数据元素中选出最小(或最大)的一个元素,存在序列的最开始位置,然后在剩下的部分找最小(大)的元素,依次交换。
排序图像【来自维基百科】:
分析
例:
原始数据 | 8 | 6 | 3 | 1 | 4 | 7 | 9 | 2 | 5 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
第一次查找 | 1 | 6 | 3 | 8 | 4 | 7 | 9 | 2 | 5 | 10 |
第二次查找 | 1 | 2 | 3 | 8 | 4 | 7 | 9 | 6 | 5 | 10 |
.... | ||||||||||
第十次查找 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
-
第一次查找,第一个位置处,找到最小的为 1 所以 8与 1交换。 -
第二次查找,第二个位置处,找到次小的为 2 所以在第二个位置的 6 与在第八个位置的 6交换。 -
以此类推,直到循环结束。
复杂度分析
选择排序的交换操作介于0和 (n-1) 次之间。选择排序的 比较操作 为 n(n-1)/2次。选择排序的赋值操作介于0和3(n-1) 次之间。
比较次数 O( ) ,比较次数与关键字的初始状态无关,总的比较次数N=(n-1) + (n-2) + ... + 1 = n x (n-1)/2 。交换次数 O(n) ,最好的情况是,已经有序,交换次数0次;最坏的情况是,逆序,交换n-1 次。交换次数比冒泡排序少,由于交换所需CPU时间比比较所需的CPU时间多,n 值较小的时候,选择排序比冒泡排序快。
原地操作几乎是选择排序的唯一优点,当空间复杂度要求较高时,可以考虑选择排序;实际使用的场合非常的罕见。
用Python实现如下
def selection_sort(list_num):
for i in range(0, len(list_num) - 1):
min_ = i
for j in range(i + 1, len(list_num)):
if list_num[j] < list_num[min_]:
min_ = j
list_num[i], list_num[min_] = list_num[min_], list_num[i] # swap
插入排序
数据结构 数组
最坏时间复杂度 O(
)
最优时间复杂度 O(n)
平均时间复杂度 O(
)
最坏空间复杂度 总共O(n) 需要辅助空间O(1)
文字描述:构建有序的序列,选取未排序的数据,在有序序列中扫描,找到相应的位置并插入,以此类推。
排序图像【来自维基百科】:
分析
例:
原始数据 | 8 | 6 | 3 | 1 | 4 | 7 | 9 | 2 | 5 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
第一次 | 6 | 8 | 3 | 1 | 4 | 7 | 9 | 2 | 5 | 10 |
第二次 | 6 | 3 | 8 | 1 | 4 | 7 | 9 | 2 | 5 | 10 |
第三次 | 3 | 6 | 8 | 1 | 4 | 7 | 9 | 2 | 5 | 10 |
.... | ||||||||||
最终 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
-
在第一次的开始选取的时候,默认 有序序列为 [8] ,然后 6与有序序列中的 8比较,之后的有序序列为 [6,8] -
在第二次的开始选取的时候,默认 有序序列为[6,8] ,然后 3与有序序列中的 8比较交换,之后 3 与 6 比较交换。
以此类推得到最终结果
复杂度分析
如果目标是把 n 个元素的序列升序排列,那么采用 插入排序 存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需 n-1 次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有 n(n-1) 次。插入排序的赋值操作是比较操作的次数减去 n-1 次,(因为n-1次循环中,每一次循环的比较都比赋值多一个,多在最后那一次比较并不带来赋值)。平均来说插入排序算法复杂度为O( ) 。因而,插入排序 不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千;或者若已知输入元素大致上按照顺序排列,那么插入排序还是一个不错的选择。插入排序在工业级库中也有着广泛的应用,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序(通常为8个或以下)。
用Python实现如下
def insetion_sort(list_num):
n = len(list_num)
if n == 1 : return
for i in range(1, n):
for j in range(i, 0, -1):
if list_num[j] < list_num[j - 1]:
list_num[j - 1] , list_num[j] = list_num[j] , list_num[j - 1]
else:
break
优化实现
上面的实现方式每一次比较都会进行一次交换,元素的交换执行时间要比比较是更耗时的。
优化思路:判断当前元素是否应该在此位置,而不是每次都进行交换。
def insetion_sort_fix(list_num):
n = len(list_num)
if n == 1 : return
for i in range(1, n):
tmp = list_num[i]
for j in range(i, -1, -1):
if list_num[j-1] > tmp:
list_num[j] = list_num[j - 1]
else:
break
list_num[j] = tmp
近乎有序的数据元素,插入排序要比O(nlogn) 更优,近乎于O(n) 。