Python数据结构背景知识之列表(List)

发布时间:2026/9/11 22:34:35
Python数据结构背景知识之列表(List) 专栏其他内容Python 中 enumerate 函数的妙用Python数据结构背景知识之列表ListPython数据结构背景知识之元组TuplePython数据结构背景知识之集合SetPython数据结构背景知识之字典Dictionary一、List的本质Python中的“动态数组”首先要明确一个关键认知Python中的List并非传统意义上的“链表”(ListedNode)而是一种动态数组Dynamic Array。这一点与C的vector、Java的ArrayList本质一致。1.1 底层逻辑与特性传统的静态数组如C语言的数组在初始化时必须指定固定大小且后续无法灵活扩容/缩容一旦元素数量超过数组长度就需要手动申请新的内存空间、拷贝原有元素操作繁琐且效率低下。而Python的List则解决了这一痛点其底层实现逻辑如下List会预先申请一块连续的内存空间初始容量通常较小如4个元素用于存储元素当元素数量达到当前容量上限时List会自动扩容通常扩容为原容量的1.5倍或2倍具体取决于Python版本申请一块更大的连续内存将原有元素拷贝到新空间再释放旧空间当元素数量大幅减少时List也会自动缩容部分版本支持避免内存浪费所有元素按顺序存储在连续内存中因此可以通过索引index直接访问元素这也是“数组”的核心优势。这里需要区分两个易混淆的概念容量capacity和长度length长度len(list)当前List中实际存储的元素个数随时变化容量capacity底层连续内存空间能容纳的最大元素个数由Python自动管理用户无法直接获取或修改。1.2 与链表Linked List的核心区别很多初学者会将List与链表混淆但二者的操作效率差异极大直接决定了刷题时的算法选择比如LeetCode中“删除链表的倒数第N个节点”不能用List模拟否则效率过低。具体区别如下表操作Python List动态数组链表Linked List按索引访问查O(1)直接定位内存地址O(n)需从头遍历到目标节点头部插入/删除增/删O(n)需移动所有后续元素O(1)只需修改头节点指针尾部插入/删除增/删O(1)无需移动元素除非扩容O(n)需遍历到尾节点双向链表除外中间插入/删除增/删O(n)需移动插入/删除位置后的所有元素O(n)需遍历到目标位置内存占用可能有冗余扩容后未用完的空间无冗余每个节点只存储自身数据和指针总结LeetCode中涉及“频繁按索引访问、尾部增删”的题目优先用Python List涉及“频繁头部增删、中间插入删除且数据量较大”的题目需考虑用链表Python无内置链表可自定义或用collections.deque模拟双向链表。二、Python List的核心操作2.1 初始化与赋值刷题中最常用的3种初始化方式根据场景选择# 1. 空列表最常用如存储结果、临时变量 lst [] # 2. 初始化时传入元素已知初始数据如题目给出的数组 lst [1, 2, 3, 4, 5] # 3. 初始化指定长度、默认值适用于需要固定长度的场景如动态规划的dp数组 lst [0] * 5 # 结果[0, 0, 0, 0, 0] lst [None] * 3 # 结果[None, None, None]【拓展】列表推导式List Comprehension适合基于已有列表生成新列表的场景。1. 基础列表推导式最常用核心语法new_lst [表达式 for 元素 in 可迭代对象]时间复杂度与普通遍历一致O (n)。# 场景1基于已有列表生成新列表如你提到的平方操作 a [1, 2, 3, 4] b [i*i for i in a] # 结果[1, 4, 9, 16] # 场景2生成指定范围的列表替代rangeappend # 生成1-10的奇数列表Hot100中“两数之和”“三数之和”常用来构造测试用例 odd_nums [x for x in range(1, 11) if x % 2 ! 0] # 结果[1, 3, 5, 7, 9] # 场景3过滤列表元素如LeetCode“移除元素”预处理 nums [3, 2, 2, 3] target 3 filtered_nums [num for num in nums if num ! target] # 结果[2, 2]2. 带条件判断的列表推导式核心语法new_lst [表达式1 if 条件 else 表达式2 for 元素 in 可迭代对象]适用于需要 “按需生成元素” 的场景。# 场景LeetCode“调整数组元素”类题目 nums [1, 2, 3, 4, 5] # 偶数乘2奇数保持不变 adjusted_nums [num*2 if num % 2 0 else num for num in nums] # 结果[1, 4, 3, 8, 5] # 场景生成n阶单位矩阵矩阵乘法/线性代数相关题目常用 n 4 # 生成4x4单位矩阵对角线元素(ij)为1其余为0 matrix [[1 if i j else 0 for j in range(n)] for i in range(n)] # 结果 # [[1, 0, 0, 0], # [0, 1, 0, 0], # [0, 0, 1, 0], # [0, 0, 0, 1]]2.2 核心增删改查操作1查按索引访问、遍历按索引访问lst[i]索引从0开始支持负索引lst[-1]表示最后一个元素时间复杂度O(1)遍历元素两种常用方式时间复杂度均为O(n)# 方式1直接遍历元素最常用无需关注索引 for num in lst: print(num) # 方式2遍历索引元素需要索引时用如双指针题目 for i, num in enumerate(lst): print(i, num)如果不了解enumerate()函数的使用可以查看我的另一篇博客Python 中 enumerate 函数的妙用查找元素位置lst.index(target)返回第一个匹配元素的索引无匹配元素则报错时间复杂度O(n)刷题时常用target in lst判断元素是否存在时间复杂度O(n)。2改修改指定索引元素lst[index] new_val直接通过索引修改时间复杂度O(1)例如lst [1, 2, 3] lst[1] 4 # 结果[1, 4, 3]排序函数sort(keyNone, reverseFalse)让lst.sort(reverseTrue)可以降序排序。按列表中每个元素的第0项即第一个参数升序排列intervals [[3,6],[2,2],[15,1]] intervals.sort(keylambda x:x[0]) print(intervals) # [[2, 2], [3, 6], [15, 1]]按列表中每个元素的第1项即第二个参数升序排列intervals [[1,6],[8,2],[15,1]] intervals.sort(keylambda x:x[1]) print(intervals) # [[15, 1], [8, 2], [1, 6]]其中lambda x: x[0]是匿名函数语法格式为lambda 参数 : 返回表达式等价于def f(x): return x[0]3增添加元素尾部添加lst.append(val)时间复杂度O(1)除非扩容扩容时为O(n)但平均复杂度仍为O(1)「高频」指定位置插入lst.insert(index, val)时间复杂度O(n)需移动后续元素刷题时尽量避免频繁使用会导致效率低下合并两个列表lst1.extend(lst2)将lst2的元素添加到lst1尾部时间复杂度O(k)k为lst2的长度优于lst1 lst2会创建新列表时间复杂度O(nk)。4删删除元素按索引删除del lst[index]时间复杂度O(n)需移动后续元素「高频」删除最后一个元素lst.pop()时间复杂度O(1)「高频」按索引删除lst.pop(index)时间复杂度O(n)按元素删除lst.remove(val)删除第一个匹配的元素无匹配元素则报错时间复杂度O(n)清空列表lst.clear()时间复杂度O(n)释放所有元素。2.3 常用进阶操作切片Slicelst[start:end:step]截取列表的一部分返回新列表时间复杂度O(k)k为切片长度。lst [1, 2, 3, 4, 5] lst[1:3] # 从索引1到2不包含3[2, 3] lst[::2] # 步长为2取所有奇数索引[1, 3, 5] lst[::-1] # 步长为-1反转列表[5, 4, 3, 2, 1]⚠️ 注意切片会创建新列表若列表过大频繁切片会占用额外内存可考虑用双指针替代。排序lst.sort()原地排序修改原列表时间复杂度O(nlogn)sorted(lst)返回新的排序后列表原列表不变「高频」。刷题时常用lst.sort(keylambda x: x)自定义排序规则如按元素绝对值排序、按二维数组的第二列排序。去重list(set(lst))先将列表转为集合自动去重再转回列表时间复杂度O(n)但会打乱原有的元素顺序若需保留原顺序可结合字典Python 3.7 字典有序lst [3, 1, 2, 1, 3, 4] # 转集合去重 → 无序 s set(lst) # 结果可能是 {1,2,3,4}顺序不固定 # 转回列表 → 顺序被打乱 new_lst list(s) print(new_lst) # 可能输出 [1,2,3,4] 或 [2,1,4,3] 等和原顺序不一致 lst [2, 1, 2, 3, 1] lst_unique list(dict.fromkeys(lst)) # 保留原顺序[2, 1, 3]统计元素出现次数lst.count(val)时间复杂度O(n)适用于需要统计频率的题目总结分类函数 / 方法核心作用简单示例增append(x)在列表末尾添加单个元素 x直接修改原列表lst [1,2]; lst.append(3)→[1,2,3]extend(iter)把可迭代对象如列表、字符串的元素逐个添加到列表末尾lst [1,2]; lst.extend([3,4])→[1,2,3,4]insert(idx, x)在指定索引idx位置插入元素 x后面元素后移lst [1,3]; lst.insert(1,2)→[1,2,3]删remove(x)删除列表中第一个出现的元素 x无该元素则报错lst [1,2,2,3]; lst.remove(2)→[1,2,3]pop([idx])删除指定索引idx的元素默认删最后一个返回被删除的元素lst [1,2,3]; lst.pop(1)→ 返回2列表变为[1,3]clear()清空列表所有元素原列表变为空lst [1,2]; lst.clear()→[]del lst[idx]关键字删除指定索引 / 切片的元素直接修改原列表lst [1,2,3]; del lst[1]→[1,3]改lst[idx] x直接修改指定索引位置的元素值lst [1,2]; lst[1] 3→[1,3]reverse()反转列表元素顺序直接修改原列表lst [1,2,3]; lst.reverse()→[3,2,1]sort(keyNone, reverseFalse)对列表排序默认升序reverseTrue降序直接修改原列表lst [3,1,2]; lst.sort()→[1,2,3]lst.sort(reverseTrue)→[3,2,1]查index(x, [start, end])查找元素 x 在列表中第一个出现的索引指定 start/end 限定范围无则报错lst [1,2,3]; lst.index(2)→1count(x)统计元素 x 在列表中出现的次数lst [1,2,2,3]; lst.count(2)→2len(lst)内置函数返回列表元素个数lst [1,2,3]; len(lst)→3x in lst运算符判断元素 x 是否在列表中返回布尔值1 in [1,2,3]→True4 in [1,2,3]→False复制copy()浅拷贝列表返回新列表原列表修改不影响新列表lst [1,2]; new_lst lst.copy()→new_lst [1,2]lst[:]切片方式浅拷贝和 copy () 等价lst [1,2]; new_lst lst[:]→new_lst [1,2]其他copy.deepcopy(lst)需导入 copy 模块深拷贝嵌套列表也完全复制import copy; lst [[1],2]; new_lst copy.deepcopy(lst)sorted(lst)内置函数返回排序后的新列表原列表不变lst [3,1,2]; sorted(lst)→[1,2,3]lst 仍为[3,1,2]enumerate(lst)内置函数返回迭代器包含 (索引元素) 对for idx, val in enumerate([1,2]): print(idx, val)→ 0 1 / 1 2max(lst)/min(lst)内置函数返回列表中最大 / 最小值元素需可比较max([1,2,3])→3min([1,2,3])→1sum(lst)内置函数返回列表所有元素的和元素需为数字sum([1,2,3])→6修改原列表 vs 返回新列表直接修改原列表的方法append()、extend()、insert()、remove()、pop()、reverse()、sort()返回新列表 / 值的copy()、sorted()、index()、count()、len()、max()、min()、sum()。三、注意事项在做List相关算法题目时很多人会因忽略细节导致代码超时或出错避免频繁在List头部插入/删除元素头部操作时间复杂度为O(n)若数据量较大如n10^4频繁操作会导致超时慎用lst lst2合并列表每次合并都会创建新列表若需多次合并优先用extend()lst lst2的底层逻辑不是 “在原列表上追加”而是申请一块新的内存空间大小 len(lst) len(lst2)把lst的所有元素复制到新空间再把lst2的所有元素复制到新空间返回这个新列表原列表lst本身不会改变。如果写lstlstlst2则相当于把lst这个变量名重新指向新列表原列表的内存会被垃圾回收。而lst.extend(lst2)的逻辑是直接在lst原有的内存空间后 “扩容”按需申请少量新内存而非全部把lst2的元素逐个追加到lst末尾不复制 lst 原有元素直接修改原列表无新列表创建。3. 注意List的可变特性函数中修改传入的List会直接修改原列表因为List是引用类型若需保留原列表需传入切片lst.copy()或lst[:]4. 排序后索引变化对List排序后元素的原始索引会被打乱若题目需要保留原始索引如“两数之和”需提前存储索引与元素的对应关系5. 边界条件处理刷题时务必考虑List为空len(lst) 0、List只有一个元素、索引越界等边界情况这是避免WAWrong Answer的关键。