全面深入了解python(四)

全面深入了解python(四)

1. 用bisect来管理已排序的序列

bisect模块包含两个主要函数,bisect和insort,两个函数都利用二分查找算法在有序序列中查找或插入元素。

1.1 用bisect来搜索

bisect(haystack, needle),其中haystack必须是一个有序的序列,把needle插入位置之后,haystack还能保持升序。也就是说在这个函数返回位置前面的值,都小于或等于needle的值。

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]

def demo(bisect_fn):
    for needle in reversed(needles):
        #用特定的bisect函数计算元素应该出现的位置
        position = bisect_fn(haystack,needle)
        #利用该位置来算出需要几个分隔符号
        offset = position * '  |'
        #打印元素和其应该出现的位置
        print(f'{needle:2d} @ {position:2d}    {offset}{needle:<2d}')
if __name__ == '__main__':
    #选用bisect函数
    bisect_fn = bisect.bisect
	#打印函数抬头
    print('demo:', bisect_fn.__name__)
    print('haystack ->',' '.join('%2d' % n for n in haystack))
    demo(bisect_fn)

运行如下:

demo: bisect
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 

每一行以needle @ position(元素及其应该插入的位置)开始,然后展示了该元素在原序列中的物理位置。

bisect的表现可以从两个方面来改变:

  • 首先可以用它的两个可选参数——lo和hi——来缩小搜寻的范围。lo的默认值是0,hi的默认值是序列的长度,即len()作用于该序列的返回值。
  • 其次bisect函数其实是bisect_right函数的别名,其还有个bisect_left的姊妹函数。区别在于bisect_left返回的插入位置是原序列中跟被插入元素相等的元素的位置,也就是新元素会被放置于它相等的元素的前面,而bisect_right返回的则是跟它相等的元素之后的位置。这个细微的差别可能对于整个序列没有用,但是对值相等形式不同的数据类型来讲,结果就不一样了。例:1==1.0的返回值是True,1和1.0其实是两个不同的元素.

这里用bisect_left运行看看结果:

...
if __name__ == '__main__':
    #选用bisect函数
    bisect_fn = bisect.bisect_left
	#打印函数抬头
    print('demo:', bisect_fn.__name__)
    print('haystack ->',' '.join('%2d' % n for n in haystack))
    demo(bisect_fn)

运行如下:

demo: bisect_left
haystack ->  1  4  5  6  8 12 15 20 21 23 23 26 29 30
31 @ 14      |  |  |  |  |  |  |  |  |  |  |  |  |  |31
30 @ 13      |  |  |  |  |  |  |  |  |  |  |  |  |30
29 @ 12      |  |  |  |  |  |  |  |  |  |  |  |29
23 @  9      |  |  |  |  |  |  |  |  |23
22 @  9      |  |  |  |  |  |  |  |  |22
10 @  5      |  |  |  |  |10
 8 @  4      |  |  |  |8 
 5 @  2      |  |5 
 2 @  1      |2 
 1 @  0    1 
 0 @  0    0 

如果这2个例子有些抽象,那么下面举一个简单偏实际的例子:

def grade(score,breakpoints = [60,70,80,90], grades = 'FDCBA'):
    i = bisect.bisect(breakpoints,score)
    return grades[i]
if __name__ == '__main__':
    print([grade(score) for score in [33,99,87,62,55,80,100,90]])

这个例子建立一个用数字作为索引的查询表格,把分数和成绩对应起来。

['F', 'A', 'B', 'D', 'F', 'B', 'A', 'A']
1.2 用bisect.insort插入新元素

排序很耗时,因此在得到一个有序序列之后我们最好能够保持它的有序。

Insort(seq,item)把变量item插入到序列seq中,并保持seq的升序顺序。

import bisect
import random

SIZE = 7
random.seed(1397)
my_list = []
for i in range(SIZE):
    new_item = random.randrange(SIZE*2)
    bisect.insort(my_list,new_item)
    print('%2d ->' % new_item,my_list)

运行如下:

 1 -> [1]
 5 -> [1, 5]
 3 -> [1, 3, 5]
13 -> [1, 3, 5, 13]
 6 -> [1, 3, 5, 6, 13]
11 -> [1, 3, 5, 6, 11, 13]
 6 -> [1, 3, 5, 6, 6, 11, 13]

insort跟bisect一样,有lo和hi两个可选参数来控制查找范围,相同的也有个变体叫insort_left,这个变体在背后用的是bisect_left.

目前所提到的内容都不仅仅是对列表或元组有效,还可以应用于几乎所有的序列类型上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值