插入排序
1. 前言
前几天在打高教杯数学建模国赛,然后最近又在缓解数学建模通宵遗留下来后遗症,所以一直没有更新。
先简单说说这次2020高教杯数学建模国赛的感受吧。我们选的是B题——穿越沙漠。一道分析+代码的综合性路径规划题,其实我们刚开始走入了一个误区,以为这道题目应该是算法比重偏大,所以一开始就在那里写智能性算法,可是我写了一晚之后,发现算法过于复杂,所以第二天早上就有点崩溃了,甚至产生了换题的想法,好在队长坚持做这道题,最后没有换题。后来我看到网上一些人对于这道题目的评价,说了一些思路比如动态规划啥的,就说很简单,但我觉得那是因为他们没有深入去做这道题目,当他们真的去写算法了,就会发现里面的条件多得吓人,数据也不少…最后我们以三天吃了三顿饭,睡了8个小时的代价,终于把题目做好,交了上去,但是现在看看,其实我们只做了题目要求的,而没有扩展,所以成绩可能不会理想。
好了,进入正题吧。今天要介绍的排序算法——插入排序是Low B三人组排序算法中的最后一个排序算法了。
2. 算法思想
和前面两个排序方法一样,我们同样将列表或者数组分为有序区和无序区。主要想法: 按照顺序,每次取出一个数,如果左边有值,就和左边的数从右到左依次比较,如果比当前数大,就将该数往右移一位,知道找到一位比手上数小的,放在该数的右边。说得比较抽象,举个例子就好理解了。
3. 例子
用插入排序将列表[3, 2, 4, 1, 5, 7, 9, 6, 8]
进行排序:
- 第一步: 取出列表第二个数即索引
index=1
的数2
,将这个数和左边的数3
比较,如果比3
大,就还是放在当前位置中,要是比3
小,就把3
往右移,然后把2
放入3
的位置,即index=0
的位置。所以第一步结束后,得到的结果为[2, 3, 4, 1, 5, 7, 9, 6, 8]
,即现在前两个位置即index=0、1
都属于有序区。 - 第二步: 第一步取的是索引为
index=1
的数,那就按顺序继续往下取,即取index=2
的数或者可以理解为第一步已经出现了有序区index=0、1
,现在我们就从无序区中取数,去和有序区的数进行比较。所以这一步取出无序区第一个数4
,将4
和左边的数即有序区的数依次比较,先和3
比较,发现大于3
就直接不需要动了,直接把4
放在原本的位置。所以这一步得到的是[2, 3, 4, 1, 5, 7, 9, 6, 8]
,此时有序区为[2, 3, 4]
,无序区为[1, 5, 7, 9, 6, 8]
。 - 第三步: 继续从无序区中取数,所以取的数为1,将1和有序区的数进行比较,
1
先和4
比较,1<4
,所以将4
右移;将1
和3
比较,1<3
,所以3
也右移;将1
和2
比较,1<2
,所以2
也右移,所以最后只剩下最左边的位置即index=0
的位置没有值了,此时,就把1
放入。该步得到的结果为:[1, 2, 3, 4, 5, 7, 9, 6, 8]
。有序区为[1, 2, 3, 4, 5]
,无序区为[7, 9, 6, 8]
. - 第四步: 从无序区中取出
7
,7
和5
比较,5<7
,所以7
保持原位不动。得到结果为:[1, 2, 3, 4, 5, 7, 9, 6, 8]
,有序区为:[1, 2, 3, 4, 5, 7]
,无序区为:[9, 6, 8]
- 第五步: 从无序区中取出数9,9和7比较,7<9,所以9不动。得到结果为:
[1, 2, 3, 4, 5, 7, 9, 6, 8]
,有序区为:[1, 2, 3, 4, 5, 7, 9]
,无序区为:[6, 8]
- 第六步: 从无序区中取出
6和9
比较,9>6
,则将9
右移一位;继续将6和7进行比较,7>6
,将7
右移一位;将6
和5
进行比较,5<6
,则此时把6
放在5
的右边,得到的结果为:[1, 2, 3, 4, 5, 6, 7, 9, 8]
,有序区为:[1, 2, 3, 4, 5, 6, 7, 9]
,无序区为: [8] - 第7步: 从无序区中取出
8
和9
进行比较,9>8
,则9
右移一位;再将8
和7
比较,8>7
,则8
就放在7
的右边即可,得到的结果为:[1, 2, 3, 4, 5, 6, 7, 8, 9]
,此时无序区已经没有值了,证明排序完成!
这里解释一下,为什么第一步是从第二个数即index=1
的数开始取的。因为我们每次都是把取出的数和其左边的数进行比较,如果取index=0
的数,那么该数的左边没有数,就不需要进行比较,所以我们的算法就直接省略了这一步,直接从index=1
开始了。
4. 代码
接着来看看代码吧:
def insert_sort(li):
for i in range(1, len(li)): # i 表示取出的数的下标
temp = li[i]
j = i - 1 # j 指的是有序区中我们要比较的数的下标
while j >= 0 and li[j] > temp: # j>0保证了左边有值,li[j] > temp是要将左边的数右移的条件,即如果左边的数>取出的数,就将左边的数右移
li[j+1] = li[j] # 左边的数右移
j -= 1 # 从右往左,继续在无序区中找数
li[j + 1] = temp # 直到所有的数都往右移了,只剩下最左边即Index=0的位置没数了或者当左边的数<取出的数了,就将取出的数放在该位置
print(li)
使用一下:
def insert_sort(li):
for i in range(1, len(li)): # i 表示摸到的牌的下标
temp = li[i]
j = i - 1 # j 指的是手里的牌的下标
while j >= 0 and li[j] > temp:
li[j+1] = li[j] # 摸到的牌左边的牌右移
j -= 1
li[j + 1] = temp
print(li)
li = [3, 2, 4, 1, 5, 7, 9, 6, 8]
insert_sort(li)
结果为:
[2, 3, 4, 1, 5, 7, 9, 6, 8]
[2, 3, 4, 1, 5, 7, 9, 6, 8]
[1, 2, 3, 4, 5, 7, 9, 6, 8]
[1, 2, 3, 4, 5, 7, 9, 6, 8]
[1, 2, 3, 4, 5, 7, 9, 6, 8]
[1, 2, 3, 4, 5, 7, 9, 6, 8]
[1, 2, 3, 4, 5, 6, 7, 9, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]