关于python中‘bisect管理已排序序列’记

本文介绍了Python的`bisect`模块如何用于管理已排序的序列,包括`bisect`函数在查找和保持序列升序时的作用,以及`bisect.insort`函数如何插入元素并保持有序。内容涵盖了`bisect`库、`sys`库和`random`库的使用,以及在Python脚本中利用`if name == 'main'`来控制代码执行的情况。

# 用 bisect 管理已排序的序列

bisect(haystack, needle) 在 haystack(干草垛)里搜索 needle(指针)的位置,该位置满足的条件是,把needle插入这个位置后,haystack 还能保持升序
也就是说在这个函数返回的位置的前面的值,都小于或者等于needle的值,其中haystack必须是一个有序的序列
可以先用bisect查找位置index,再用haystack.insert(index, needle)插入新值,也可以直接使用后者
调用 bisect库 , sys 库及random库
import bisect
import sys
import random
num_hay = [1, 4, 5, 6, 8, 10, 19, 23, 23, 35]   # 定义被插入数列
num_need = [0, 1, 2, 5, 8, 10, 14, 20, 23, 38]  # 定义插入数列的数列
row_fmt = '{0:2d} @ {1:2d}    {2}{0:<2d}'   # 定义数据显示格式


def demo(bisect_fn):
    """定义测试bisect的函数"""
    for needle in reversed(num_need):   # reversed() 返回一个反转的迭代器即将数列倒序。只能进行一次循环遍历。显示一次所包含的值!
        position = bisect_fn(num_hay, needle)   # 使用bisect(haystack, needle)函数查询指针位置(第1个数之前为0,第1个数之后为1,第2个数之后为2 )
        offset = position * '  |'   # 通过 pisition 计算制表需要的 “  |” 数
        print(row_fmt.format(needle, position, offset))     # 将数据填入定义好的数据显示格式(row_fmt)中


if __name__ == '__main__':
    if sys.argv[-1] == 'left':   # 执行脚本文件时,python3 脚本文件名 left  即定义数值从位置左侧插入
        bisect_fn_1 = bisect.bisect_left
    else:
        bisect_fn_1 = bisect.bisect     # bisect.bisect 实际上就是bisect.bisect_right 前者是别名
    print('DEMO:', bisect_fn_1.__name__)    # 打印上面判断的函数名
    print('haystack ->', ' '.join('%2d' % n for n in num_hay))      # str.join()通过指定字符连接序列中元素后生成的新字符串。str为指定字符
    demo(bisect_fn_1)   # 调用函数demo

在这里插入图片描述

一个python的文件有两种使用的方法,第一是直接作为脚本执行,第二是import到其他的python脚本中被调用(模块重用)执行。
而当一个.py文件作为模块被导入时,我们可能不希望一部分代码被运行。
所以在if name == ‘main’: 下的代码只有在第一种情况下(即文件作为脚本直接执行)才会被执行,而import到其他脚本中是不会被执行的。
所以如果说定义的某一部分代码只需要在本脚本文件中使用的情况下,就写在if name == ‘main’:语句下。

# bisect可以用来建立一个用数字作为索引的查询表格,比如说把分数和成绩对应起来

def grade(score):
    """定义函数grade() 的默认值,score表示要查询的成绩,breakpoints表示成绩划分的界限,grades表示对应输出的成绩等级。"""
    breakpoints = [60, 70, 80, 90]
    grades = 'FDCBA'
    a = bisect.bisect(breakpoints, score)    # 利用bisect()函数来找出在breakpoints中的位置
    return grades[a]    # 然后利用对应返回值,作为索引打印grades中的对应成绩等级


print([grade(score) for score in [33, 58, 67, 85, 97]])

在这里插入图片描述

# bisect.insort 插入新元素

insort(seq, item)把变量item插入到有序序列seq中,并能保持seq的升序顺序。
调用bisect库以及random库(顶端)
random.seed(4)  # random.seed() 表示固定随机生成的数列,不会每次都变。seed()的值任意指定。
my_list = []
for i in range(7):
    new_items = random.randrange(14)
    bisect.insort(my_list, new_items)
    print(str('{:2d} ->'.format(new_items)), my_list)

在这里插入图片描述
本文为作者学习整理笔记,文章来源:《流畅的python》

<think>我们被要求解释Pythonbisect模块的使用方法。根据引用,bisect模块用于处理有序序列,提供二分查找算法来定位插入位置和插入元素,同时保持序列有序。 主要功能: 1. 二分查找:bisect_left, bisect_right (或bisect,它是bisect_right的别名) 2. 插入元素:insort_left, insort_right (或insort,它是insort_right的别名) 注意:bisect模块不会检查列表是否有序,因此使用前必须确保列表是有序的,否则结果不可预测。 我们将通过示例说明这些函数的用法。</think># Pythonbisect模块的详细使用指南 `bisect`模块是Python标准库中用于**处理有序序列**的实用工具,它基于二分查找算法实现,能够高效地在有序列表中定位元素位置并插入新元素,同时保持列表的有序性。 ## 一、核心功能与方法 ### 1. 二分查找函数 | 函数名 | 描述 | 时间复杂度 | |--------|------|------------| | `bisect_left(a, x)` | 返回第一个**大于等于**x的元素位置 | $O(\log n)$ | | `bisect_right(a, x)` | 返回第一个**大于**x的元素位置 | $O(\log n)$ | | `bisect(a, x)` | `bisect_right`的别名 | $O(\log n)$ | ### 2. 有序插入函数 | 函数名 | 描述 | 时间复杂度 | |--------|------|------------| | `insort_left(a, x)` | 在第一个大于等于x的位置插入 | $O(n)$ | | `insort_right(a, x)` | 在第一个大于x的位置插入 | $O(n)$ | | `insort(a, x)` | `insort_right`的别名 | $O(n)$ | > **注意**:插入操作的时间复杂度主要来自列表插入操作,二分查找本身只需$O(\log n)$时间[^4]。 ## 二、使用示例 ### 1. 基本定位操作 ```python import bisect data = [1, 3, 5, 7, 9] # 查找插入位置 print(bisect.bisect_left(data, 4)) # 输出: 2 (5的位置) print(bisect.bisect_right(data, 5)) # 输出: 3 (7的位置) # 检查元素是否存在 x = 5 pos = bisect.bisect_left(data, x) if pos < len(data) and data[pos] == x: print(f"{x} 在索引 {pos} 处") # 5 在索引 2 处 else: print(f"{x} 不存在") ``` ### 2. 有序插入操作 ```python data = [1, 3, 5, 7, 9] # 插入元素 bisect.insort(data, 4) bisect.insort(data, 6) bisect.insort(data, 5) # 允许重复值 print(data) # 输出: [1, 3, 4, 5, 5, 6, 7, 9] ``` ### 3. 处理重复值 ```python data = [1, 2, 2, 2, 3, 4] # 在第一个2的位置插入 bisect.insort_left(data, 2) print(data) # [1, 2, 2, 2, 2, 3, 4] # 在最后一个2之后插入 bisect.insort_right(data, 2) print(data) # [1, 2, 2, 2, 2, 2, 3, 4] ``` ## 三、实际应用场景 ### 1. 成绩分段统计 ```python def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'): i = bisect.bisect(breakpoints, score) return grades[i] print(grade(65)) # 'D' print(grade(85)) # 'B' ``` ### 2. 区间查找 ```python intervals = [(1, 5), (6, 10), (11, 15)] starts = [i[0] for i in intervals] def find_interval(x): pos = bisect.bisect_left(starts, x) if pos < len(intervals) and intervals[pos][0] <= x <= intervals[pos][1]: return f"{x} 在区间 {intervals[pos]} 中" return f"{x} 不在任何区间" print(find_interval(7)) # 7 在区间 (6, 10) 中 print(find_interval(3)) # 3 在区间 (1, 5) 中 print(find_interval(12)) # 12 在区间 (11, 15) 中 ``` ### 3. 维护实时有序数据流 ```python class OrderedStream: def __init__(self): self.data = [] def add(self, value): bisect.insort(self.data, value) def get_range(self, start, end): left = bisect.bisect_left(self.data, start) right = bisect.bisect_right(self.data, end) return self.data[left:right] stream = OrderedStream() for num in [5, 2, 8, 3, 1]: stream.add(num) print(stream.get_range(3, 6)) # 输出: [3, 5] ``` ## 四、性能对比 | 操作 | 普通方法 | bisect模块 | 提升倍数 | |------|----------|------------|---------| | 10万元素查找 | $O(n)$ | $O(\log n)$ | 约17,000倍 | | 10万元素插入 | $O(n)$ | $O(n)$ | 查找部分提升17,000倍 | | 100万次查询 | 分钟级 | 毫秒级 | 千倍级 | > **数据说明**:在100,000个元素的有序列表中,二分查找仅需约17次比较,而线性查找平均需50,000次比较[^2]。 ## 五、使用注意事项 1. **必须确保列表有序**:使用前务必对列表排序 ```python data = [9, 2, 5, 1, 7] data.sort() # 先排序! bisect.insort(data, 4) ``` 2. **处理大型数据集**: ```python import bisect import numpy as np # 创建100万元素的有序数组 large_data = np.sort(np.random.randint(0, 1000000, 1000000)).tolist() # 高效查找 position = bisect.bisect_left(large_data, 500000) ``` 3. **自定义对象支持**: ```python class Student: def __init__(self, name, score): self.name = name self.score = score def __lt__(self, other): return self.score < other.score students = [Student('Alice', 85), Student('Bob', 92)] students.sort() bisect.insort(students, Student('Charlie', 88)) ``` `bisect`模块是处理有序数据的利器,特别适合需要频繁查询和插入的场景,能显著提升程序性能[^1][^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值