流畅的Python读书笔记(五)序列:序列的排序及管理

流畅的Python读书笔记(四)序列:列表的排序及管理

概述

文章简单介绍了python中列表的排序list.sortsorted方法,主要讲解了管理有序序列的库bisect

列表排序

说到列表排序,对于刚学习Python的同学来说,很快就能想到list.sortsorted。下面只是简单再说明一下这两个函数。

  • list.sort(self, key=None, reverse=False)方法会就地排序列表,而不会创建原列表的一份拷贝,再排序。

  • sorted(iterable, key=None, reverse=False)方法会新建一个列表并返回,该对象已排好序。注意:①参数iterable可以接受任何形式的可迭代对象,甚至包括不可变序列或生成器。②不管sorted接受的是什么样的参数,最后都会返回一个列表,该列表包含了iterable中的所有元素。

顺带一提的小知识:Python及Java7之后的标准库中的排序算法使用的都是TimSort算法。Timsort是一种混合、稳定高效的排序算法,源自合并排序和插入排序,旨在很好地处理多种真实数据。

列表管理

此处所说的列表管理,就是当我们向已经排好序的列表中增加元素后,仍然维持列表的有序性。

管理有序序列很简单,已有现成的库可供我们使用——bisectbisect的底层算法为二分查找,比较简单,感兴趣可以去看看源码。

bisect中主要包含两个函数bisect.bisectbisect.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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值