python的set去重依靠哈希表实现,平均时间复杂度o(1),要求元素可哈希且满足hash与==一致性;去重本质是边插入边判重,不保序,不可含列表等可变类型。

Python 的 set 去重靠的是哈希表(hash table)实现,不是简单遍历比较。它平均时间复杂度为 O(1),插入、查找、删除都很快,但元素必须是可哈希的(immutable)。
set 底层用哈希表,不是链表或树
Python 的 set 内部基于字典(dict)的键结构演化而来,底层是一个开放寻址(open addressing)的哈希表。每个元素通过 hash() 计算哈希值,再映射到固定大小的数组索引位置。如果发生哈希冲突(不同对象算出相同哈希),Python 会用探测序列(如伪随机偏移)找下一个空槽,而不是拉链法。
- 所有元素必须可哈希:字符串、数字、元组(内部全不可变)可以;列表、字典、集合本身不行
- 哈希值在对象生命周期内必须不变,所以可变对象不能放入 set
- 两个相等对象(
a == b)必须有相同哈希值(hash(a) == hash(b)),这是 Python 哈希协议的要求
去重过程其实是一次次“插入”而非“扫描”
写 set(lst) 去重时,并不是先建空 set 再逐个判断是否存在,而是对每个元素调用 set.add():先算 hash,定位桶位,若为空则存入;若已存在相同 hash 且 == 判定为相等,则跳过——本质是“边插边判重”,没有额外的 O(n) 扫描步骤。
-
list(set(lst))不保序,因为哈希表不记录插入顺序(CPython 3.7+ 的 dict 保序,但 set 仍不保序) - 想保序去重,得用
dict.fromkeys(lst).keys()或手动遍历 + set 记录已见元素 - 空 set 初始化后容量很小(通常 8 个 slot),随着元素增加自动扩容(约翻倍),触发 rehash
面试高频问题与关键点
面试官常借 set 考察你对哈希原理、可变性、边界情况的理解,不只是语法。
立即学习“Python免费学习笔记(深入)”;
-
为什么
{[1,2], [3,4]}报错? 列表不可哈希 →TypeError: unhashable type: 'list' -
set([1, 2, 2, 3])结果是{1, 2, 3},但它是怎么知道第二个 2 要丢掉的? 插入第二个 2 时,hash 值一样,且2 == 2为 True,判定重复,不插入 -
两个自定义类实例,
==相等但hash()不同,放进 set 会怎样? 会当成两个不同元素(违反哈希协议),导致逻辑错误;正确做法是同时重写__eq__和__hash__ - 海量数据去重,内存不够怎么办? set 全放内存,此时要考虑外部排序、布隆过滤器(近似去重)、分块处理或用数据库/Redis
小结:快是因为哈希,稳是因为协议
set 去重快,靠的是哈希表的平均 O(1) 操作;可靠,靠的是 Python 对可哈希对象的严格约束和哈希-相等一致性要求。理解这些,才能答好面试题,也才能避开运行时陷阱。










