insert(0, x)需移动全部元素,因列表是连续内存的动态数组,插入时原索引0至n-1元素必须依次后移一位,时间复杂度o(n);而append()直接写入预留空间,均摊o(1)。

因为 append() 只在末尾写入,而 insert(0, x) 必须把全部已有元素往后挪一位。
为什么 insert(0, x) 要移动所有元素
Python 列表底层是动态数组,内存连续。在索引 0 处插入,意味着原索引 0 的元素要变成索引 1,原索引 1 变成索引 2……直到最后一个元素变成索引 n。这个过程是逐个复制,时间复杂度为 O(n)。
- 对长度为 10 万的列表执行一次
insert(0, x),就要移动 10 万个对象引用 - 即使只插一个整数,移动的是指针,不是值本身,但开销仍与列表长度线性相关
- CPython 不做“懒位移”或延迟重排——插入即位移,没有缓冲或跳表优化
append() 为什么几乎不花时间
append() 直接写进预留空间(over-allocation),不碰已有元素。
- CPython 默认多分配约 12.5%~25% 的容量,比如当前存了 128 个元素,实际分配可能达 144–160 个槽位
- 只要没填满,就是纯指针赋值 + 长度+1,无循环、无复制
- 扩容时才 realloc 内存并整体拷贝,但该成本被均摊到多次
append()上,均摊仍是 O(1) - 用
insert(len(lst), x)模拟append()?仍慢 2–3 倍——多了索引检查、边界判断和分支跳转
头部插入的替代方案有哪些
真需要头插,别硬扛 insert(0, x)。
- 先
append(),最后调list.reverse():适合一次性构建完再翻转 - 换用
collections.deque,调appendleft():头尾插入都是 O(1),但失去按索引随机访问的 O(1) 特性 - 批量头插?收集到临时列表,再用切片赋值:
lst[0:0] = temp_list:C 层优化,比循环insert(0, x)快一个数量级以上 - 需要保持有序插入?用
bisect.insort(),它内部仍调insert(),但至少省了查找时间;超大规模考虑sortedcontainers.SortedList
最易被忽略的一点:insert() 的性能陷阱不只出现在 insert(0, x),哪怕 insert(-1, x)(倒数第二位),也要移动至少一个元素,无法享受 append() 的预留空间红利。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











