飞道的博客

爬虫 (四十三) 常用标准库 bisect (三十四)

275人阅读  评论(0)

bisect模块实现了二分查找和插入算法

这个模块短小精干,简单易用,并且可以用C重写。

我们可以看一下bisect模块的源码。


   
  1. "" "Bip algorithms." ""
  2. def insort_right(a, x, lo= 0, hi=None):
  3. if lo < 0:
  4. raise ValueError( 'lo must be non-negative')
  5. if hi is None:
  6. hi = len(a)
  7. while lo < hi:
  8. mid = (lo+hi) //2
  9. if x < a[mid]: hi = mid
  10. else: lo = mid+ 1
  11. a.insert(lo, x)
  12. insort = insort_right # backward compatibility
  13. def bisect_right(a, x, lo= 0, hi=None):
  14. if lo < 0:
  15. raise ValueError( 'lo must be non-negative')
  16. if hi is None:
  17. hi = len(a)
  18. while lo < hi:
  19. mid = (lo+hi) //2
  20. if x < a[mid]: hi = mid
  21. else: lo = mid+ 1
  22. return lo
  23. bisect = bisect_right # backward compatibility
  24. def insort_left(a, x, lo= 0, hi=None):
  25. if lo < 0:
  26. raise ValueError( 'lo must be non-negative')
  27. if hi is None:
  28. hi = len(a)
  29. while lo < hi:
  30. mid = (lo+hi) //2
  31. if a[mid] < x: lo = mid+ 1
  32. else: hi = mid
  33. a.insert(lo, x)
  34. def bisect_left(a, x, lo= 0, hi=None):
  35. if lo < 0:
  36. raise ValueError( 'lo must be non-negative')
  37. if hi is None:
  38. hi = len(a)
  39. while lo < hi:
  40. mid = (lo+hi) //2
  41. if a[mid] < x: lo = mid+ 1
  42. else: hi = mid
  43. return lo
  44. # Overwrite above definitions with a fast C implementation
  45. try:
  46. from _bisect import *
  47. except ImportError:
  48. pass

这可能是Python初学者少有的能快速看懂的标准库源代码。整个模块去掉注释语句,就这么多行代码。

bisect = bisect_right这一行其实就是个alias,用于向后兼容。

最后的try、except语句为我们提供了用C语言重写算法的钩子。

方法介绍

bisect模块采用经典的二分算法查找元素。模块提供下面几个方法:

bisect.bisect_left(a, x, lo=0, hi=len(a))

定位x在序列a中的插入点,并保持原来的有序状态不变。参数lo和hi用于指定查找区间。如果x已经存在于a中,那么插入点在已存在元素的左边。函数的返回值是列表的整数下标。

bisect.bisect_right(a, x, lo=0, hi=len(a))

和上面的区别是插入点在右边。函数的返回值依然是一个列表下标整数。

bisect.bisect(a, x, lo=0, hi=len(a))

等同于bisect_right()。

注意,前面这三个方法都是获取插入位置,也就是列表的某个下标的,并不实际插入。但下面三个方法实际插入元素,没有返回值。

bisect.insort_left(a, x, lo=0, hi=len(a))

将x插入有序序列a内,并保持有序状态。相当于a.insert(bisect.bisect_left(a, x, lo, hi), x)。碰到相同的元素则插入到元素的左边。这个操作通常是 O(log n) 的时间复杂度。

bisect.insort_right(a, x, lo=0, hi=len(a))

同上,只不过碰到相同的元素则插入到元素的右边。

bisect.insort(a, x, lo=0, hi=len(a))

等同于insort_right()。

实例展示:


   
  1. import bisect
  2. x = 200
  3. list1 = [ 1, 3, 6, 24, 55, 78, 454, 555, 1234, 6900]
  4. ret = bisect.bisect(list1, x)
  5. print( "返回值:", ret)
  6. print( "list1 = ", list1)
  7. #------------------------------
  8. 运行结果:
  9. 返回值: 6
  10. list1 = [ 1, 3, 6, 24, 55, 78, 454, 555, 1234, 6900]
  11. ##########################################################
  12. import bisect
  13. x = 200
  14. list1 = [ 1, 3, 6, 24, 55, 78, 454, 555, 1234, 6900]
  15. ret = bisect.insort(list1, x)
  16. print( "返回值:", ret)
  17. print( "list1 = ", list1)
  18. #------------------------------------------
  19. 运行结果:
  20. 返回值: None
  21. list1 = [ 1, 3, 6, 24, 55, 78, 200, 454, 555, 1234, 6900]

下面是一个bisect和random配合的例子:


   
  1. import bisect
  2. import random
  3. random.seed( 1)
  4. print( 'New Pos Contents')
  5. print( '--- --- --------')
  6. l = []
  7. for i in range( 1, 15):
  8. r = random.randint( 1, 100)
  9. position = bisect.bisect(l, r)
  10. bisect.insort(l, r)
  11. print( '%3d %3d' % (r, position), l)
  12. #------------------------------------------
  13. 打印结果:
  14. New Pos Contents
  15. --- --- --------
  16. 18 0 [ 18]
  17. 73 1 [ 18, 73]
  18. 98 2 [ 18, 73, 98]
  19. 9 0 [ 9, 18, 73, 98]
  20. 33 2 [ 9, 18, 33, 73, 98]
  21. 16 1 [ 9, 16, 18, 33, 73, 98]
  22. 64 4 [ 9, 16, 18, 33, 64, 73, 98]
  23. 98 7 [ 9, 16, 18, 33, 64, 73, 98, 98]
  24. 58 4 [ 9, 16, 18, 33, 58, 64, 73, 98, 98]
  25. 61 5 [ 9, 16, 18, 33, 58, 61, 64, 73, 98, 98]
  26. 84 8 [ 9, 16, 18, 33, 58, 61, 64, 73, 84, 98, 98]
  27. 49 4 [ 9, 16, 18, 33, 49, 58, 61, 64, 73, 84, 98, 98]
  28. 27 3 [ 9, 16, 18, 27, 33, 49, 58, 61, 64, 73, 84, 98, 98]
  29. 13 1 [ 9, 13, 16, 18, 27, 33, 49, 58, 61, 64, 73, 84, 98, 98]

下面的5个例子是利用bisect模块实现通用的列表元素查询方法:


   
  1. def index(a, x):
  2. '定位最左边的值等于x的元素的下标'
  3. i = bisect_left(a, x)
  4. if i != len(a) and a[i] == x:
  5. return i
  6. raise ValueError
  7. def find_lt(a, x):
  8. '获取最靠右的值小于x的元素'
  9. i = bisect_left(a, x)
  10. if i:
  11. return a[i -1]
  12. raise ValueError
  13. def find_le(a, x):
  14. '获取最靠右的值小于等于x的元素'
  15. i = bisect_right(a, x)
  16. if i:
  17. return a[i -1]
  18. raise ValueError
  19. def find_gt(a, x):
  20. '获取最靠左边的值大于x的元素'
  21. i = bisect_right(a, x)
  22. if i != len(a):
  23. return a[i]
  24. raise ValueError
  25. def find_ge(a, x):
  26. '获取最靠左边的值大于或等于x的元素'
  27. i = bisect_left(a, x)
  28. if i != len(a):
  29. return a[i]
  30. raise ValueError

下面是一个利用bisect自动由百分制成绩转换为ABCD等级的方法:90 以上是‘A’, 80 -89 是‘B’,


   
  1. >>> def grade(score, breakpoints=[ 60, 70, 80, 90], grades= 'FDCBA'):
  2. ... i = bisect(breakpoints, score)
  3. ... return grades[i]
  4. ...
  5. >>> [grade(score) for score in [ 33, 99, 77, 70, 89, 90, 100]]
  6. [ 'F', 'A', 'C', 'C', 'B', 'A', 'A']

另外一个例子


   
  1. >>> data = [( 'red', 5), ( 'blue', 1), ( 'yellow', 8), ( 'black', 0)]
  2. >>> data.sort(key=lambda r: r[ 1])
  3. >>> keys = [r[ 1] for r in data] # precomputed list of keys
  4. >>> data[bisect_left(keys, 0)]
  5. ( 'black', 0)
  6. >>> data[bisect_left(keys, 1)]
  7. ( 'blue', 1)
  8. >>> data[bisect_left(keys, 5)]
  9. ( 'red', 5)
  10. >>> data[bisect_left(keys, 8)]
  11. ( 'yellow', 8)

请继续关注我

记得点赞加关注哦,记得加鸡腿啊


转载:https://blog.csdn.net/qq_36772866/article/details/104151606
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场