Skip to content

数据类型与内置结构

list/dict/set/tuple 底层实现与复杂度、推导式。

Updated View as Markdown
For humans

数据类型与内置结构

四种内置容器是 Python 的日常主力。面试主线:每种结构的底层实现、复杂度、什么时候用哪个。

底层实现与复杂度

结构 底层 查找 插入/追加 特点
list 动态数组(连续内存) O(n) 尾部摊还 O(1),头部 O(n) 按下标访问 O(1)
dict 哈希表 O(1) 均摊 O(1) 均摊 3.7+ 保插入序
set 哈希表(只存键) O(1) 均摊 O(1) 均摊 去重、集合运算
tuple 定长数组 O(n) 不支持 不可变、可哈希

list 是动态数组:底层是连续内存的指针数组(ob_item),所以按下标访问 O(1)。扩容策略是倍增(约 1.125 倍),所以 append 摊还 O(1);insert(0, x) 要搬所有元素,O(n)。

dict/set 是哈希表:键算哈希定位桶,冲突用开放寻址(CPython)。均摊 O(1),但扩容(load factor 超 2/3)时要 rehash,单次操作可能 O(n)。dict 的键必须可哈希(不可变对象),这就是不可变类型能当键的原因。

dict 从 3.7 起保证插入顺序(3.6 是 CPython 实现细节,3.7 成为语言规范)。这不是“有序字典”(不按键排序),只是插入序。

推导式

squares = [x * x for x in range(10)]          # 列表推导
even = {x for x in nums if x % 2 == 0}        # 集合推导
mapping = {k: v for k, v in pairs}            # 字典推导
gen = (x * x for x in range(10))              # 生成器表达式(惰性!)
  • 推导式比 for 循环快(C 层执行),可读性好
  • 生成器表达式是惰性的:不一次生成全部,适合大序列(见迭代器篇)
  • 嵌套推导可读性差,超过两层用普通循环或拆函数

常用操作与陷阱

操作 复杂度 说明
x in list O(n) 大 list 判存在很慢,换 set
x in dict/set O(1) 查重的正确结构
list.count / index O(n) 遍历
dict.get(k, default) O(1) 安全取值
collections.deque 两端 O(1) 需要双端操作时替代 list

高频优化点:“判断是否存在”一律用 set,list 的 in 是 O(n),数据量大时差别巨大。

面试追问

  1. list 为什么 append 快、insert(0) 慢? 动态数组:尾部追加摊还 O(1)(倍增扩容),头部插入要搬移全部元素 O(n)
  2. dict 怎么做到 O(1)? 哈希表:键哈希定位桶,开放寻址处理冲突。均摊 O(1),扩容 rehash 时单次 O(n)
  3. dict 有序吗? 3.7+ 保证插入顺序,不是排序序。需要按键排序用 sorted() 或 OrderedDict
  4. 为什么 dict 的 key 要不可变? 哈希表依赖键的哈希值稳定,可变对象哈希会变,破坏查找
  5. 列表推导和生成器表达式的区别? 前者立即生成全部(占内存),后者惰性逐个产出(省内存)。大序列用生成器
Navigation

Type to search…

↑↓ navigate↵ selectEsc close