vlambda博客
学习文章列表

【算法1】选择排序 - 插入排序

选择排序法

数据结构 数组
最坏时间复杂度 О(n²)
最优时间复杂度 О(n²)
平均时间复杂度 О(n²)
最坏空间复杂度 总共О(n),需要辅助空间O(1)

文字描述:从待排序的数据元素中选出最小(或最大)的一个元素,存在序列的最开始位置,然后在剩下的部分找最小(大)的元素,依次交换。

排序图像【来自维基百科】:

【算法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)

文字描述:构建有序的序列,选取未排序的数据,在有序序列中扫描,找到相应的位置并插入,以此类推。

排序图像【来自维基百科】:

【算法1】选择排序 - 插入排序

【算法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) 。


老科,谢谢你!