流畅的Python读书笔记(四)序列:列表的排序及管理
概述
文章简单介绍了python中列表的排序list.sort
、sorted
方法,主要讲解了管理有序序列的库bisect
。
列表排序
说到列表排序,对于刚学习Python
的同学来说,很快就能想到list.sort
和sorted
。下面只是简单再说明一下这两个函数。
-
list.sort(self, key=None, reverse=False)
方法会就地排序列表,而不会创建原列表的一份拷贝,再排序。 -
sorted(iterable, key=None, reverse=False)
方法会新建一个列表
并返回,该对象已排好序。注意:①参数iterable
可以接受任何形式的可迭代对象,甚至包括不可变序列或生成器。②不管sorted
接受的是什么样的参数,最后都会返回一个列表,该列表包含了iterable
中的所有元素。
顺带一提的小知识:Python及Java7之后的标准库中的排序算法使用的都是TimSort
算法。Timsort
是一种混合、稳定高效的排序算法,源自合并排序和插入排序,旨在很好地处理多种真实数据。
列表管理
此处所说的列表管理,就是当我们向已经排好序的列表中增加元素
后,仍然维持列表的有序性。
管理有序序列很简单,已有现成的库可供我们使用——bisect
。bisect
的底层算法为二分查找
,比较简单,感兴趣可以去看看源码。
bisect
中主要包含两个函数bisect.bisect
和bisect.insort
,前者返回将要插入元素应该插入的位置,后者直接插入要插入的元素。
-
bisect.bisect(a, x, lo=0, hi=None)
-
参数介绍:
a
是要被排序的对象,x
是要插入的元素,lo,hi
是可选参数,分别表示搜索范围的边界。如果lo,hi
为默认,则函数会将x
应该插入的位置下标返回,搜索的范围是a[lo:hi]
,其中lo=0,hi=len(a)
。 -
特殊情况:当要插入的元素事先已存在于
a
中时,bisect.bisect
返回的下标应该是存在于a
中的与x
相等的元素下标的右侧,即返回值为a.index(x)+1
. -
对特殊情况的解释:从
bisect
模块的源码中可以看出bisect.bisect
函数有两个版本,一是bisect.bisect.bisect_left
,另一个是bisect.bisect.bisect_right
,这两个函数只是在处理特殊情况时有所不同,想必你也能猜到它们的区别,就是遇到特殊情况时,是返回a.index(x)
还是a.index(x)+1
。
# bisect模块中的代码,所以 # bisect.bisect就是bisect.bisect_right # Create aliases bisect = bisect_right insort = insort_right
-
使用示例:
import bisect
import random
random.seed(0) #设定种子,保证随机生成数可再现
n = 10 #随机生成10个数,构成列表
a = [random.randrange(10) for i in range(n)]
a.sort() #先对列表进行排序(升序)
print(a)
index = bisect.bisect(a, 6)
print("要插入的位置:", index)
index = bisect.bisect(a, 10)
print("要插入的位置:", index)
结果:注意,插入元素6
时,原列表中有3个6
,根据结果可以判断,返回的数值为最右侧6
的下标加1.
[0, 4, 4, 5, 6, 6, 6, 7, 7, 8]
要插入的位置: 7
要插入的位置: 10
-
bisect.insort(a, x, lo=0, hi=None)
-
参数介绍:参数与上面的
bisect.bisect
基本一致,不做介绍。唯一的区别就是insort
是直接插入元素。 -
遇到特殊情况(即有与
x
相等的元素位于a
中时),处理方式与前面介绍的一致。只不过,insort
方法会直接将元素插入。 -
同样的
insort()
方法也有两个版本,insort_right
,insort_left
。
# Create aliases bisect = bisect_right insort = insort_right # insrot就是insort_right的别名
-
使用示例:
import bisect
import random
random.seed(0) #设定种子,保证随机生成数可再现
n = 10 #随机生成10个数,构成列表
a = [random.randrange(10) for i in range(n)]
a.sort() #先对列表进行排序(升序)
print(a)
print("插入元素6,要插入的位置:", bisect.bisect(a, 6))
bisect.insort(a, 6),
print("插入元素后,列表变为:")
print(a)
print("插入元素10,要插入的位置:", bisect.bisect(a, 10))
bisect.insort(a, 10)
print("插入元素后,列表变为:")
print(a)
结果:
[0, 4, 4, 5, 6, 6, 6, 7, 7, 8]
插入元素6,要插入的位置: 7
插入元素后,列表变为:
[0, 4, 4, 5, 6, 6, 6, 6, 7, 7, 8]
插入元素10,要插入的位置: 11
插入元素后,列表变为:
[0, 4, 4, 5, 6, 6, 6, 6, 7, 7, 8, 10]
例题
以下是流畅的Python书中给出的例子,可以拿来练练手,看看前面所讲的知识掌握了没有。拷贝代码,自行运行。以下代码演示了排序对象变化的过程。
用bisect来搜索
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)
offset = position * ' | '
print(ROW_FMT.format(needle, position, offset))
if __name__ == '__main__':
if sys.argv[-1] == 'left':
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)
用bisect.insort插入新元素
import bisect
import random
SIZE=7
random.seed(1729)
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)
参考资料
- 流畅的Python