赞
踩
Python bisect 内置模块
文档
bisect 是 python 内置模块,用于有序序列的插入和查找。
bisect,是实现 二分 (bisection) 算法 的模块,能够保持序列顺序不变的情况下对其进行 二分查找和插入,适合用于降低对冗长序列查找的时间成本。当然也可以通过 “以空间换时间” 的方式,例如用于构造 hashmap 的 Counter 类 。
>>> import bisect
>>> dir(bisect)
[..., 'bisect', 'bisect_left', 'bisect_right', 'insort', 'insort_left', 'insort_right']
1
2
3
名称 功能
bisect_left() 查找 目标元素左侧插入点
bisect_right() 查找 目标元素右侧插入点
bisect() 同 bisect_right()
insort_left() 查找目标元素左侧插入点,并保序地 插入 元素
insort_right() 查找目标元素右侧插入点,并保序地 插入 元素
insort() 同 insort_right()
注意,使用 bisect 模块的方法之前,要求操作对象是 有序序列(升、降)。
在有序列表 seq 中查找 item 插入的位置,并且返回其索引 (index),使得插入 item 后序列依然保持有序;有两个可选参数 lo 和 hi 来缩小搜索范围,lo 的默认值为 0 ,hi 的默认值为序列的长度。
返回的位置索引 又称为 “插入点”,将其设为 i,则序列 a 可以被划分为两部分,切片表示左侧 a[lo, i) 和右侧 a[i, hi] 。
1、bisect_left()
bisect.bisect_left(a, x, lo=0, hi=len(a))
1
若 x ∈ a,返回最左侧 x 的索引;否则返回与 x 右邻居索引,使得 x 若插入后能位于其 左侧。
2、bisect_right() 、bisect()
bisect.bisect(a, x, lo = 0, hi =len(a))
bisect.bisect_right(a, x, [lo=0, hi=len(a)])
1
2
返回与 x(最右侧 x)右邻居索引,使得 x 若插入后能位于其 右侧。
3、insort_left()
bisect.insort_left(a, x, lo=0, hi=len(a))
1
bisect.bisect_left() 在序列 a 中 查找 元素 x 的插入点 (左侧)
bisect.insort_left() 就是在找到插入点的基础上,将元素 x 插入序列 a,从而改变序列 a 同时保持元素顺序。
4、insort_right()、insort()
bisect.insort(a, x, lo=0, hi=len(a))
bisect.insort_right(a, x, lo=0, hi=len(a))
1
2
bisect.bisect_right() 在序列 a 中 查找 元素 x 的插入点 (右侧)
bisect.insort_right() 就是在找到插入点的基础上,将元素 x 插入序列 a,从而改变序列 a 同时保持元素顺序。
Python bisect 应用
import bisect
nums = [0,1,2,3,4,4,6]
idx = bisect.bisect_left(nums,-1) # 0
idx = bisect.bisect_left(nums,8) # 7
idx = bisect.bisect_left(nums,3) # 3
idx = bisect.bisect_left(nums,3.5) # 4
idx = bisect.bisect_left(nums,4) # 4
idx = bisect.bisect_left(nums,3,1,4) # 3
a = [1,4,6,8,12,15,20]
position = bisect.bisect(a,13)
print(position) # 5
# 用可变序列内置的 insert 方法插入
a.insert(position, 13)
print(a) # 5 [1, 4, 6, 8, 12, 13, 15, 20]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
使用 bisect.insort,比 bisect 先查找该插入哪个位置,再用 insert 方法插入更加快速的方法
import bisect
a = [1,4,6,8,12,15,20]
bisect.insort(a, 13)
print(a) # [1, 4, 6, 8, 12, 13, 15, 20]
1
2
3
4
5
Python bisect 源码
def bisect_left(a, x, lo=0, hi=None):
"""Return the index where to insert item x in list a, assuming a is sorted.
The return value i is such that all e in a[:i] have e < x, and all e in
a[i:] have e >= x. So if x already appears in the list, a.insert(x) will
insert just before the leftmost x already there.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
若 x ∈ a,返回最左侧 x 的索引;否则返回与 x 右邻居索引,使得 x 若插入后能位于其 左侧。
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if a[mid] < x: lo = mid+1
else: hi = mid
return lo
def bisect_right(a, x, lo=0, hi=None):
"""Return the index where to insert item x in list a, assuming a is sorted.
The return value i is such that all e in a[:i] have e <= x, and all e in
a[i:] have e > x. So if x already appears in the list, a.insert(x) will
insert just after the rightmost x already there.
Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
返回与 x(最右侧 x)右邻居索引,使得 x 若插入后能位于其 右侧。
"""
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if x < a[mid]: hi = mid
else: lo = mid+1
return lo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
bisect() 和 bisect_right() 以及 insort() 和 insort_right() 功能相同的原因:
在源码的最下面存在这样两句代码:
# Create aliases
bisect = bisect_right
insort = insort_right
1
2
3
所以 bisect 就是 bisect_right 方法的一个引用,可是说是别名, 同理 insort 是 insert_right 方法的引用。
3.10.4 Documentation » Python 标准库 » 数据类型 » bisect — 数组二分查找算法
这个模块对有序列表提供了支持,使得他们可以在插入新数据仍然保持有序。对于长列表,如果其包含元素的比较操作十分昂贵的话,这可以是对更常见方法的改进。这个模块叫做 bisect 因为其使用了基本的二分(bisection)算法。
定义了以下函数:
bisect.bisect_left(a, x, lo=0, hi=len(a), *, key=None) # 在 3.10 版更改: 增加了 key 形参。
1
在 a 中找到 x 合适的插入点以维持有序。参数 lo 和 hi 可以被用于确定需要考虑的子集;默认情况下整个列表都会被使用。如果 x 已经在 a 里存在,那么插入点会在已存在元素之前(也就是左边)。如果 a 是列表(list)的话,返回值是可以被放在 list.insert() 的第一个参数的。
返回的插入点 i 将数组 a 分成两半,使得 all(val < x for val in a[lo : i]) 在左半边而 all(val >= x for val in a[i : hi]) 在右半边。
key specifies a key function of one argument that is used to extract a comparison key from each element in the array. To support searching complex records, the key function is not applied to the x value.
If key is None, the elements are compared directly with no intervening function call.
bisect.bisect_right(a, x, lo=0, hi=len(a), *, key=None)
bisect.bisect(a, x, lo=0, hi=len(a), *, key=None)
1
2
类似于 bisect_left(),但是返回的插入点是 a 中已存在元素 x 的右侧。
返回的插入点 i 将数组 a 分成两半,使得左半边为 all(val <= x for val in a[lo : i]) 而右半边为 all(val > x for val in a[i : hi])。
bisect.insort_left(a, x, lo=0, hi=len(a), *, key=None)
1
按照已排序顺序将 x 插入到 a 中。
此函数首先会运行 bisect_left() 来定位一个插入点。 然后,它会在 a 上运行 insert() 方法在正确的位置插入 x 以保持排序顺序。
To support inserting records in a table, the key function (if any) is applied to x for the search step but not for the insertion step.
bisect.insort_right(a, x, lo=0, hi=len(a), *, key=None)¶
bisect.insort(a, x, lo=0, hi=len(a), *, key=None)
1
2
类似于 insort_left(),但是把 x 插入到 a 中已存在元素 x 的右侧。
此函数首先会运行 bisect_right() 来定位一个插入点。 然后,它会在 a 上运行 insert() 方法在正确的位置插入 x 以保持排序顺序。
性能说明
当使用 bisect() 和 insort() 编写时间敏感的代码时,请记住以下概念。
二分法对于搜索一定范围的值是很高效的。 对于定位特定的值,则字典的性能更好。
insort() 函数的时间复杂度为 O(n) 因为对数时间的搜索步骤被线性时间的插入步骤所主导。
这些搜索函数都是 无状态的 并且会在它们被使用后丢弃键函数的结果。 因此,如果在一个循环中使用搜索函数,则键函数可能会在同一个数据元素上被反复调用。 如果键函数速度不快,请考虑用 functools.cache() 来包装它以避免重复计算。 另外,也可以考虑搜索一个预先计算好的键数组来定位插入点(如下面的示例节所演示的)。
参见
Sorted Collections 是一个使用 bisect 来管理数据的已排序多项集的高性能模块。
SortedCollection recipe 使用 bisect 构建了一个功能完整的多项集类,拥有直观的搜索方法和对键函数的支持。 所有键函数都 是预先计算好的以避免在搜索期间对键函数的不必要的调用。
搜索有序列表
上面的 **bisect() 函数对于找到插入点是有用的,但在一般的搜索任务中可能会有点尴尬。**下面 5 个函数展示了如何将其转变成有序列表中的标准查找函数
def index(a, x):
'Locate the leftmost value exactly equal to x'
i = bisect_left(a, x)
if i != len(a) and a[i] == x: return i
raise ValueError
def find_lt(a, x):
'Find rightmost value less than x'
i = bisect_left(a, x)
if i: return a[i-1]
raise ValueError
def find_le(a, x):
'Find rightmost value less than or equal to x'
i = bisect_right(a, x)
if i: return a[i-1]
raise ValueError
def find_gt(a, x):
'Find leftmost value greater than x'
i = bisect_right(a, x)
if i != len(a): return a[i]
raise ValueError
def find_ge(a, x):
'Find leftmost item greater than or equal to x'
i = bisect_left(a, x)
if i != len(a): return a[i]
raise ValueError
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
例子
函数 bisect() 还可以用于数字表查询。这个例子是使用 bisect() 从一个给定的考试成绩集合里,通过一个有序数字表,查出其对应的字母等级:90 分及以上是 ‘A’,80 到 89 是 ‘B’,以此类推
>>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
... i = bisect(breakpoints, score)
... return grades[i]
>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']
1
2
3
4
5
The bisect() and insort() functions also work with lists of tuples. The key argument can serve to extract the field used for ordering records in a table:
>>> from collections import namedtuple
>>> from operator import attrgetter
>>> from bisect import bisect, insort
>>> from pprint import pprint
>>> Movie = namedtuple('Movie', ('name', 'released', 'director'))
>>> movies = [
... Movie('Jaws', 1975, 'Speilberg'),
... Movie('Titanic', 1997, 'Cameron'),
... Movie('The Birds', 1963, 'Hitchcock'),
... Movie('Aliens', 1986, 'Scott')
... ]
>>> # Find the first movie released on or after 1960
>>> by_year = attrgetter('released')
>>> movies.sort(key=by_year)
>>> movies[bisect(movies, 1960, key=by_year)]
Movie(name='The Birds', released=1963, director='Hitchcock')
>>> # Insert a movie while maintaining sort order
>>> romance = Movie('Love Story', 1970, 'Hiller')
>>> insort(movies, romance, key=by_year)
>>> pprint(movies)
[Movie(name='The Birds', released=1963, director='Hitchcock'),
Movie(name='Love Story', released=1970, director='Hiller'),
Movie(name='Jaws', released=1975, director='Speilberg'),
Movie(name='Aliens', released=1986, director='Scott'),
Movie(name='Titanic', released=1997, director='Cameron')]
If the key function is expensive, it is possible to avoid repeated function calls by searching a list of precomputed keys to find the index of a record:
>>>
>>> data = [('red', 5), ('blue', 1), ('yellow', 8), ('black', 0)]
>>> data.sort(key=lambda r: r[1]) # Or use operator.itemgetter(1).
>>> keys = [r[1] for r in data] # Precompute a list of keys.
>>> data[bisect_left(keys, 0)]
('black', 0)
>>> data[bisect_left(keys, 1)]
('blue', 1)
>>> data[bisect_left(keys, 5)]
('red', 5)
>>> data[bisect_left(keys, 8)]
('yellow', 8)
————————————————
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。