文章简单介绍了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)