哈希表替代内层线性查找可将嵌套循环从o(n²)降至o(n),双指针适用于已排序序列,itertools.product节省笛卡尔积空间,递归需缓存剪枝,降维前应先用cprofile定位瓶颈。

用哈希表替代内层线性查找
嵌套循环性能差的主因往往是内层循环在重复做 O(n) 的查找。比如判断两个列表是否有交集,或统计元素频次时反复遍历——这时把内层遍历换成哈希表(dict 或 set)查表,能把内层从 O(n) 降到 O(1) 平均复杂度。
常见错误是仍保留二重 for 循环结构,只改了内容没动骨架;或者误用 list 做成员判断(if x in my_list),实际仍是 O(n)。
- 先用
set(my_list)或{k: v for ...}预处理高频查询数据 - 内层循环中所有
in判断、计数累加、存在性检查,全部转向哈希结构 - 注意:哈希表构建本身是
O(n),但只做一次,摊还后整体从O(n²)降为O(n)
双指针法适用于已排序序列的两路遍历
当两个输入序列天然有序(如日志时间戳、索引数组),或可低成本排序时,双指针能彻底消除嵌套。它用两个变量分别扫描两个序列,根据比较结果决定哪个指针前进,避免暴力配对。
典型陷阱是强行给无序数据排序再双指针——排序本身是 O(n log n),若原嵌套已是 O(n²),可能不划算;更糟的是边排序边修改原结构,导致逻辑错乱。
- 确认输入是否已排序,或排序代价是否低于嵌套开销
- 双指针变量命名要体现语义,如
i和j不够清晰,改用left_idx、right_ptr更易维护 - 边界条件必须显式处理:某一方走到末尾时,另一方剩余元素是否还需处理?
用 itertools.product 或生成器延迟展开笛卡尔积
不是所有嵌套都该被“消灭”——当业务逻辑确实需要两两组合(如坐标网格、参数组合穷举),硬拆成单层会牺牲可读性。此时重点不是去掉嵌套,而是控制其爆炸规模。
直接写 for a in A: for b in B: 会在内存中隐式维持完整笛卡尔积状态;而 itertools.product(A, B) 返回迭代器,每次只产一个元组,空间复杂度从 O(|A|×|B|) 降到 O(1)。
- 避免提前转成
list(itertools.product(...)),这会立刻触发全量生成 - 若需中途 break,注意
itertools.product不支持索引访问,得靠计数器或条件判断退出 - 当
A或B是生成器(如文件逐行读取),务必确认它们可多次迭代;否则需缓存为list
递归转迭代 + 缓存剪枝防重复子问题
多层嵌套常来自递归展开(如回溯、DFS),此时降维关键不是扁平化循环,而是识别重复子问题并缓存。Python 的 @lru_cache 能自动拦截相同参数调用,避免指数级重复计算。
容易忽略的是缓存键的可哈希性:如果递归参数含 list、dict 等不可哈希类型,@lru_cache 直接报错 TypeError: unhashable type;还有人把缓存放在函数内部手动管理,却忘了清除旧状态,导致内存泄漏。
- 把可变参数转成
tuple或字符串(如tuple(sorted(items)))再传入缓存函数 - 设置
maxsize参数,避免缓存无限增长,例如@lru_cache(maxsize=128) - 调试时临时设
maxsize=None并打印func.cache_info(),验证命中率
真正卡住性能的往往不是循环层数本身,而是每层里没意识到的隐式遍历、重复计算或数据结构误用。降维前先用 cProfile 定位热区,比盲目重构更有效。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











