我个人在插入排序的实现上有点晕,有点要懂不懂的感觉,我本着不完全弄明白不罢休的精神,强行来写写插入排序来说服自己。
原理说明
首先来说说插入排序的原理:插入排序是将列表中的元素逐一与已经排序好的元素进行比较,即每一次都拿一个没有排序的元素,然后插入到已经排了序的元素中去,通过比较,插入在一个合适的位置。
比如有一个列表:[ 33, 34, 25, 62, 32 ],要进行升序排列
这样来看,插入排序的原理其实很好理解,很容易就懂了,对吧。
我也觉得很好理解,然后用代码实现的时候我就把我自己绕晕了,我都开始怀疑我的智商了。。。
好不容易在参考了资料后搞了出来,然后过了下再看代码,我又晕了,所以我感觉我还是理解的不够深,我需要把它刻在脑子里,所以我决定来死磕一把代码逻辑,至少要说服我自己!
代码解析
先贴出算法主要代码:
for i in range(1, len(li)): #---------(1)
tmp = li[i] #---------(2)
no = i - 1 #---------(3)
while no >= 0 and tmp < li[no]: #---------(4)
li[no+1] = li[no] #---------(5)
no -= 1 #---------(6)
li[no+1] = tmp #---------(7)
代码中的 li 就是需要排序的列表。
算法主要实现就只有7行代码,但是要彻底说服自己其实并不容易,哈哈哈,现在一行一行来死磕。
这里还是用一个伪例子来说明,有一个列表:li = [ A, B, C, D, E ],该列表中A, B, C, D, E分别代表一个数字,我们要完成升序排列(下面逐一解释每一行代码)。
- (1).首先来看需要进行多少次循环,即多少轮的比较。每一轮都需要插入一个数,但是插入的第一个数(列表的第一个元素)不用和任何数进行比较,所以直接从第二个数(列表的第二个元素,索引为1)开始,一直到列表的最后一个元素,最后一个元素的索引为列表长度减1,因为range的第2个参数是取不到的,所以直接用range(1, len(li))。
- (2).我们将当前需要插入的数先保存起来,让它先“置身事外”,因为之后很可能会改变该数在列表中位置的值。按照这个逻辑,那么第一次的for循环就将B存了起来。
- (3).no = i - 1,这个no可以理解为获取当前值所在位置的前一个位置的索引,这里B的索引为1,那么no就是0,0索引位置的数就是A,因为B要和A做比较嘛。
- (4).这个while循环就是将需要插入的数与已经排好序的数逐一进行比较,no >= 0这个判断条件就是表示还剩有未与将要插入的数进行比较的排序好了的数,tmp < li[no]这里就是在将插入的数与排序好的某个数进行比较。
- (5).如果插入的值小于了当前比较的值,说明插入的值肯定是在该比较的值的前面,即假如这里B < A,那么说明B的位置肯定要在A的前面,至于A前面还有没有其他的数这不重要,只要知道B应该在A前面了,就将A往后面推一位,好给B腾出一个位置,即li[no+1] = li[no]。如果A前面还有数字的话,下一轮while循环再将B与A前面的数字进行比较,直到while循环结束。
- (6).no -= 1就是去获取A前面的数字的索引,如果这里no小于0了,那么就说明A前面没有数字了,就跳出循环进行下一行代码(7),说明B是最小数,将其放在A原来所在的位置(第一位)。如果no依然大于等于0那么说明A前面还有数字,那么再次进入while循环,将要插入的数(B)与之比较。当要插入的数字大于了正在与之比较的数字,也将跳出循环,说明B没有必要再往前移动了,即当前与之比较的数字也没有必要往后面推一位给B腾位置了。
- (7).最后一步,将要插入的值放在它应该在的位置,由于在(6)跳出循环前no多减了一个1,所以这里要加一个1才是插入值应该在的位置。
嗯嗯,差不多了,就是这样,感觉把自己说服了,同时也加深了印象,哈哈哈。
哦对了,插入排序的空间复杂度是最佳的,因为只使用了一个额外的内存空间,即代码中的用于临时存储插入值的tmp。
完整实现代码:
# 插入排序
from random import randint
li = [randint(1, 100) for n in range(10)]
print("初始列表:", li)
for i in range(1, len(li)): # 因为开始就需要拿第2个元素来和第一个元素比较,所以从索引为1的元素(即列表的第二个元素)开始
tmp = li[i] # 将要插入的值保存起来
no = i - 1
while no >= 0 and tmp < li[no]: # 将要插入的值依次和该值前面的所有值进行比较
li[no+1] = li[no]
no -= 1
li[no+1] = tmp
print("第%s次排序后:" % i, li)
print("完成排序后:", li)
运行结果如下:
希望对大家也有帮助,哈哈哈。