需求
希望将一个数据插入到一个有序列表中,插入后不改变整个序列的顺序。
从下面的例子可以看出,插入新数据的列表仍保持升序。
a=[0,4,7,10,15]
b=14
# 最终得到[0,4,7,14,15]
上面表红的是需要注意的两点:
- 待插入的数据必须是int类型
- 列表中保存的是int类型数据,并且按照升序排列
解决方法
使用python内置模块bisect。
import bisect
将数据插入到列表的时候,最常见的是两个需求:
- 不要将数据插入列表,只需要返回数据在有序列表中的位置
- 需要将数据插入列表中,并且保持列表的有序性
需求1:返回位置索引
bisect.bisect_left(a, x, lo=0, hi=len(a))
其中,a是有序列表,x是目标数据,返回值是x在a中的位置索引。如果x与a中的数据相等,则返回相等数据左侧的位置索引。
举例说明,可以将a中的元素看做分割点,将整个有理数空间分成len(a)+1份,假设a=[a1,a2,a3,a4],则划分后的空间范围为:
[sub_space] (negtive_inf,a1] (a1,a2] (a2,a3] (a3,a4] (a4,postive_inf)
| | | | | |
[index] 0 1 2 3 4
如果目标数据在子空间范围内,就会返回相应的位置索引;如果恰好等于边界值,则返回左侧的位置索引,即子空间的左边界是不包含关系,右边界是包含关系,这正是“left”的含义。比如x=a2,返回a2左侧的索引,即(a1,a2],索引值为1.
bisect.bisect_right(a, x, lo=0, hi=len(a))
bisect.bisect(a, x, lo=0, hi=len(a)) # bisect是bisect_right的别名
bisect_right和bisect_left的区别可以使用上面的例子来说明,唯一的区别在于如果目标数据与边界值相同,返回的是边界值右侧的索引,即子空间左边界是包含关系,右边界是不包含关系,这正是“right”的含义:
[sub_space] (negtive_inf,a1) [a1,a2) [a2,a3) [a3,a4) [a4,postive_inf)
| | | | | |
[index] 0 1 2 3 4
需求2:将数据插入有序列表并保持列表有序性
bisect.insort_left(a, x, lo=0, hi=len(a))
将x插入到a中,并保持a的有序性。如果遇到x与a中元素相等的情况,将x插入到相等元素的左侧。
bisect.insort_right(a, x, lo=0, hi=len(a))
bisect.insort(a, x, lo=0, hi=len(a))
唯一的区别在于如果遇到x与a中元素相等的情况,将x插入到相等元素的右侧。
bisect的局限性
bisect只能处理int类型的数据,对于复杂的数据类型就无能为力了。
data = [('black', 0), ('blue', 1), ('red', 5), ('yellow', 8)]
x = ('grey', 3)
希望将x插入按照第二项的大小插入到data中,使用bisect.insort()将无法实现。