二分查找与 bisect 模块
# 二分查找算法
原理:
在haystack(必须有序)中查找needle的位置
该位置满足的条件是:
把needle插入到这个位置之后,haystack还能保持升序
也就是在这个函数返回的位置前面的值,都小于或等于needle的值。
你可以先用bisect(haystack,needle) 查找位置index,
在用haystack.insert(index,needle)来插入新值。
但也可以用insort来一步到位,并且速度更快
import bisect
import sys
HAYSTACK = [1,4,5,6,8,12,15,20,21,23,23,26,29,30]
NEEDLES = [0,1,2,5,8,10,22,23,29,30,31]
ROW_FMT = '{0:2d} @ {1:2d} {2}{0:<2d}'
def demo(bisect_fn):
for needle in reversed(NEEDLES):
position = bisect_fn(HAYSTACK,needle) # 计算元素应该出现的位置
# print(needle,position)
offset = position * ' |' # 利用位置来算出需要几个分隔符号
print(ROW_FMT.format(needle,position,offset)) # 打印元素和应该出现的位置
if __name__ == '__main__':
# print(sys.argv[-1])
if sys.argv[-1] == 'left': # 根据命令上最后一个参数来选用bisect函数
bisect_fn = bisect.bisect_left
else:
bisect_fn = bisect.bisect
print('DEMO:',bisect_fn.__name__) # 打印选中的函数
print('haystack ->',' '.join('%2d'% n for n in HAYSTACK))
demo(bisect_fn)
结果:
DEMO: bisect_right
haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30
31 @ 14 | | | | | | | | | | | | | |31
30 @ 14 | | | | | | | | | | | | | |30
29 @ 13 | | | | | | | | | | | | |29
23 @ 11 | | | | | | | | | | |23
22 @ 9 | | | | | | | | |22
10 @ 5 | | | | |10
8 @ 5 | | | | |8
5 @ 3 | | |5
2 @ 1 |2
1 @ 1 |1
0 @ 0 0
bisect :
可选参数:
lo默认值为0 hi默认值为序列长度,len()作用于该序列的返回值
bisect 函数其实是bisect.bisect_right函数的别名,
bisect.bisect_right() 与 bisect.bisect_left() 区别:
插入位置是原序列中跟被插入元素相等的元素的位置的时候:
bisect.bisect_left: 新元素会被放置于相等元素的前面
bisect.bisect_right: 新元素会被放置于相等元素的后面
这个细微的差别可能对于整数序列来说没什么区别,
但是对于那些值相等,但是形式不同的数据类型来说,结果就不一样了
例如 1 == 1.0 但是 1 和 1.0 其实是两个不同的元素
# 借助bisect可以用来建立一个用数字作为索引的查询表格
import bisect
def grade(score,breakpoints=[60,70,80,90],grades='FDCBA'):
i = bisect.bisect(breakpoints,score)
return grades[i]
print([grade(score) for score in [33,99,77,70,89,90,100]])
# 可以在很长的有序序列中作为index的替代,用来更快的查找一个元素的位置