当前位置: 首页 > news >正文

Python 的 collections 模块

1. deque (双端队列)

定义

deque(读作 “deck”,即双端队列)是一个支持从两端高效添加和删除元素的数据结构。相比列表(list)在头部操作的 O(n) 时间复杂度,deque 的两端操作都是 O(1)。

用途
  • 队列和栈:可以用 deque 实现 FIFO(队列)或 LIFO(栈)。
  • 滑动窗口:在处理固定大小的窗口问题时,deque 适合维护窗口内的元素。
  • BFS(广度优先搜索)deque 常用于实现队列。
  • 字符串或数组的双端操作:需要频繁在两端添加/删除元素时。
常用方法
  • append(x):在右端添加元素 x。
  • appendleft(x):在左端添加元素 x。
  • pop():移除并返回右端元素。
  • popleft():移除并返回左端元素。
  • extend(iterable):将可迭代对象中的元素添加到右端。
  • extendleft(iterable):将可迭代对象中的元素添加到左端(注意顺序会反转)。
  • clear():清空队列。
  • rotate(n):将队列向右旋转 n 步(n 为负数时向左旋转)。
示例代码
from collections import deque

# 创建一个双端队列
d = deque([1, 2, 3])

# 添加元素
d.append(4)       # d = deque([1, 2, 3, 4])
d.appendleft(0)   # d = deque([0, 1, 2, 3, 4])

# 删除元素
right = d.pop()       # right = 4, d = deque([0, 1, 2, 3])
left = d.popleft()    # left = 0, d = deque([1, 2, 3])

# 旋转
d.rotate(1)       # d = deque([3, 1, 2])
d.rotate(-1)      # d = deque([1, 2, 3])

print(d)  # 输出 deque([1, 2, 3])
竞赛场景

在 BFS 中,deque 是实现队列的首选。例如:

from collections import deque

def bfs(start, graph):
    queue = deque([start])
    visited = set([start])
    
    while queue:
        node = queue.popleft()
        print(node, end=' ')
        for neighbor in graph[node]:
            if neighbor not in visited:
                visited.add(neighbor)
                queue.append(neighbor)

# 示例图
graph = {
    1: [2, 3],
    2: [1, 4],
    3: [1, 5],
    4: [2],
    5: [3]
}
bfs(1, graph)  # 输出:1 2 3 4 5

2. Counter (计数器)

定义

Counter 是一个字典子类,用于统计可哈希对象(通常是字符串、数字等)的出现次数。它非常适合需要统计元素频率的问题。

用途
  • 统计频率:快速计算数组、字符串中每个元素的出现次数。
  • 前 k 个高频元素:结合堆或排序解决频率相关问题。
  • 字符匹配:检查两个字符串是否是异位词(anagram)。
  • 多重集操作:支持集合的并、交、差等操作。
常用方法
  • Counter(iterable):从可迭代对象创建计数器。
  • elements():返回一个迭代器,包含所有元素(按计数重复)。
  • most_common(n):返回前 n 个频率最高的元素及其计数。
  • subtract(iterable):从当前计数中减去另一个可迭代对象的计数。
  • update(iterable):累加另一个可迭代对象的计数。
示例代码
from collections import Counter

# 统计字符串中字符频率
s = "abracadabra"
counter = Counter(s)
print(counter)  # Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})

# 获取频率最高的 3 个字符
print(counter.most_common(3))  # [('a', 5), ('b', 2), ('r', 2)]

# 统计数组
nums = [1, 1, 2, 3, 1, 2]
counter = Counter(nums)
print(counter)  # Counter({1: 3, 2: 2, 3: 1})

# 集合操作
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2)  # Counter({'a': 4, 'b': 3})
print(c1 - c2)  # Counter({'a': 2})
竞赛场景

判断两个字符串是否是异位词:

from collections import Counter

def is_anagram(s: str, t: str) -> bool:
    return Counter(s) == Counter(t)

print(is_anagram("listen", "silent"))  # True
print(is_anagram("hello", "world"))   # False

3. defaultdict (默认字典)

定义

defaultdict 是一个字典子类,当访问不存在的键时,会自动为该键提供一个默认值(由指定的工厂函数生成)。这避免了手动检查键是否存在。

用途
  • 分组:将元素按某种规则分组(如按值、长度等)。
  • 计数:类似 Counter,但更灵活。
  • 图的邻接表:构建图结构时,自动为每个节点初始化一个空列表。
  • 动态规划:需要为不存在的键提供默认值时。
常用工厂函数
  • list:默认值为空列表 []
  • int:默认值为 0。
  • set:默认值为空集合 set()
  • 自定义函数:通过 lambda 或其他函数定义默认值。
示例代码
from collections import defaultdict

# 默认值为空列表
d = defaultdict(list)
d['a'].append(1)  # d = {'a': [1]}
d['a'].append(2)  # d = {'a': [1, 2]}
d['b'].append(3)  # d = {'a': [1, 2], 'b': [3]}
print(d)

# 默认值为 0
d = defaultdict(int)
d['a'] += 1  # d = {'a': 1}
d['b'] += 2  # d = {'a': 1, 'b': 2}
print(d)

# 自定义默认值
d = defaultdict(lambda: "unknown")
d['a'] = "known"
print(d['a'])  # known
print(d['b'])  # unknown
竞赛场景

构建图的邻接表:

from collections import defaultdict

def build_graph(edges):
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)
        graph[v].append(u)  # 无向图
    return graph

edges = [(1, 2), (2, 3), (3, 1)]
graph = build_graph(edges)
print(graph)  # defaultdict(<class 'list'>, {1: [2, 3], 2: [1, 3], 3: [1, 2]})

4. OrderedDict (有序字典)

定义

OrderedDict 是一个字典子类,能够记住键的插入顺序。在 Python 3.7+ 中,普通字典也保留了插入顺序,但 OrderedDict 提供了一些额外的功能(如 move_to_end)。

用途
  • LRU 缓存:实现最近最少使用(Least Recently Used)缓存。
  • 保持顺序:需要显式维护键值对的插入顺序。
  • 滑动窗口:某些问题需要记录窗口内元素的顺序。
常用方法
  • popitem(last=True):移除并返回最后一项(last=False 时移除第一项)。
  • move_to_end(key, last=True):将指定键移动到末尾(或开头)。
  • 其他与普通字典类似的操作。
示例代码
from/collections import OrderedDict

# 创建有序字典
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
print(od)  # OrderedDict([('a', 1), ('b', 2), ('c', 3)])

# 移动元素
od.move_to_end('a')  # OrderedDict([('b', 2), ('c', 3), ('a', 1)])
od.move_to_end('b', last=False)  # OrderedDict([('b', 2), ('c', 3), ('a', 1)])

# 删除元素
od.popitem()  # 删除最后一项,OrderedDict([('b', 2), ('c', 3)])
od.popitem(last=False)  # 删除第一项,OrderedDict([('c', 3)])
竞赛场景

实现 LRU 缓存:

from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity: int):
        self.cache = OrderedDict()
        self.capacity = capacity
    
    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        self.cache.move_to_end(key)
        return self.cache[key]
    
    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self.cache.move_to_end(key)
        self.cache[key] = value
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)

# 测试
cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
print(cache.get(1))  # 1
cache.put(3, 3)      # 移除 2
print(cache.get(2))  # -1

5. namedtuple (命名元组)

定义

namedtuple 是一个轻量级的类,用于创建具有命名字段的元组。它结合了元组的不可变性和字典的字段访问方式。

用途
  • 结构体:表示具有固定字段的数据结构(如点坐标、记录等)。
  • 清晰代码:通过字段名访问元素,代码更具可读性。
  • 节省内存:相比字典或自定义类,namedtuple 更高效。
常用方法
  • 通过字段名或索引访问元素。
  • _replace(**kwargs):返回一个新的命名元组,替换指定字段。
  • _asdict():将命名元组转换为字典。
示例代码
from collections import namedtuple

# 定义命名元组
Point = namedtuple('Point', ['x', 'y'])

# 创建实例
p = Point(1, 2)
print(p)       # Point(x=1, y=2)
print(p.x, p.y)  # 1 2
print(p[0], p[1])  # 1 2

# 替换字段
p2 = p._replace(y=3)
print(p2)  # Point(x=1, y=3)

# 转换为字典
print(p._asdict())  # {'x': 1, 'y': 2}
竞赛场景

表示二维平面上的点:

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])

def distance(p1, p2):
    return ((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) ** 0.5

p1 = Point(0, 0)
p2 = Point(3, 4)
print(distance(p1, p2))  # 5.0

总结与建议

以下是 collections 模块中常见工具的适用场景总结:

工具主要用途时间复杂度优势
deque队列、栈、BFS、滑动窗口两端操作 O(1)
Counter频率统计、异位词、Top K 问题计数 O(n),查询 O(1)
defaultdict分组、计数、图的邻接表访问不存在键时自动初始化
OrderedDictLRU 缓存、保持插入顺序移动/删除操作 O(1)
namedtuple结构体、轻量级数据表示内存高效,字段访问 O(1)
学习建议
  1. 从简单问题入手:尝试用 Counter 解决频率统计问题,用 deque 实现 BFS,用 defaultdict 构建图。
  2. 多练习竞赛题:在 LeetCode、Codeforces 等平台上,搜索与这些数据结构相关的题目(如滑动窗口、BFS、LRU 缓存)。
  3. 熟悉时间复杂度:理解每个工具的性能优势,避免在竞赛中因数据结构选择不当导致超时。
  4. 结合其他模块collections 常与 heapq(堆)、bisect(二分查找)等结合使用,逐步学习这些模块。

相关文章:

  • 【Excel】数据透视表月度数据排序不正确
  • 智能座舱测试用例编写
  • MCU开发主要是项目移植吗?
  • 基于SiamFC的红外目标跟踪
  • java 多租户的产品设计思路
  • HCIP第十三天
  • leetcode 718 最长公共子数组
  • 汉诺塔问题——用贪心算法解决
  • Java数据库连接池详解:类型、特点、区别及配置方式
  • OpenCV 关键点定位
  • 在 Java 中实现异步编程:CompletableFuture 使用指南!
  • app逆向专题五:新快报app数据采集
  • wx212基于ssm+vue+uniapp的科创微应用平台小程序
  • 7个向量数据库对比:Milvus、Pinecone、Vespa、Weaviate、Vald、GSI 和 Qdrant
  • 详解 Https 和加密、摘要、签名、数字证书
  • 第十一天 - MySQL/SQLite操作 - 数据库备份脚本 - 练习:监控数据存储系统
  • 阿里通义实验室发布图片数字人项目LAM,实现高保真重建
  • 怎么免费下载glb格式模型、和gltf格式文件模型,还可以在线编辑修改
  • 基础购物车功能总结
  • Python asyncio 入门实战-1
  • 三星“七天机”质保期内屏幕漏液被要求自费维修,商家:系人为损坏
  • 4月金融数据前瞻:受去年低基数因素影响,社融增量有望同比大幅多增
  • 金地集团:今年前4个月实现销售额109.3亿元,同比下降52.44%
  • 长三角地区中华老字号品牌景气指数发布,哪些牌子是你熟悉的?
  • 复旦发文缅怀文科杰出教授裘锡圭:曾提出治学需具备三种精神
  • 马上评|让“贾宝玉是长子长孙”争议回归理性讨论