数据结构 · #6 / 11
列表 + 元组
有序集合与必备方法
为什么重要
列表是大多数问题的默认容器。元组则安全地锁住数据。
核心思想
列表是灵活的架子;元组是密封的盒子。元组是可哈希的(可以做字典的键、集合的成员),列表则不行。
试一试
列表基础——append、extend、切片、推导式:
xs = [1, 2, 3]
xs.append(4) # in place
xs.extend([5, 6]) # in place
xs += [7] # in place too
print(xs)
# Comprehension — the Pythonic loop
squares = [x*x for x in xs if x % 2 == 0]
print(squares)
元组是不可变的,支持解包,并且可以做字典的键:
origin = (0, 0)
def move(point, dx, dy):
x, y = point # unpacking
return (x + dx, y + dy)
print(move(origin, 3, 4))
routes = { (0, 0): "home", (3, 4): "park" }
print(routes[(3, 4)])
排序——就地排序与返回新列表:
words = ["apple", "Banana", "cherry"]
print(sorted(words)) # case-sensitive
print(sorted(words, key=str.lower)) # case-insensitive
print(sorted(words, key=len, reverse=True))
# In place — returns None! (common bug)
words.sort()
print(words)
列表方法(必备完整清单)
| 方法 | 作用 | 示例 |
| --- | --- | --- |
| append(x) | 添加一个元素 | nums.append(5) |
| extend(iter) | 添加多个元素 | nums.extend([6,7]) |
| insert(i,x) | 在某索引处添加 | nums.insert(1, 42) |
| remove(x) | 移除第一个匹配项 | nums.remove(2) |
| pop(i) | 按索引移除 | nums.pop() |
| clear() | 移除全部 | nums.clear() |
| index(x) | 查找位置 | nums.index(3) |
| count(x) | 统计某值的数量 | nums.count(3) |
| sort() | 就地排序 | nums.sort() |
| reverse() | 就地反转 | nums.reverse() |
| copy() | 浅拷贝 | nums.copy() |
把列表当作栈 / 队列
列表可以充当一个不错的栈(在末尾 append / pop)。如果想要一个快速的队列,请使用 collections.deque——从列表头部弹出是 O(n):
from collections import deque
q = deque([1, 2, 3])
q.append(4) # enqueue
q.popleft() # dequeue — O(1)
拷贝与别名(重要)
a = [1, 2, 3]
b = a # alias — same list, mutations are shared
c = a.copy() # new list — independent
嵌套列表的陷阱
[[0] * 3] * 3 把同一个内层行重复了三次——修改其中一个会修改所有:
grid = [[0] * 3] * 3 # WRONG: shared rows
grid = [[0] * 3 for _ in range(3)] # correct: independent rows
快速检查
- 问:
b = a创建了什么? 答: 一个别名(同一个列表)。
小练习
- 不用
sum()求一个列表的和。 - 从一个已排序的列表中去除重复项(概念)。
- 在索引 2 处插入一个值。
该做与不该做
- 该做:单个元素用
append,多个元素用extend。 - 不该做:以为
sort()会返回一个新列表。 - 不该做:用
[[x]*n]*m来构建嵌套列表。
深入一点——复杂度速览
- 索引访问:O(1)
- 追加:均摊 O(1)
- 在头部插入/移除:O(n)
- 成员检测(
x in lst):O(n)
常见错误
- 错误: 在迭代时修改列表。 修正: 遍历一份拷贝,或使用索引。
- 错误: 把别名和拷贝搞混。 修正: 使用
list(a)或a.copy()。 - 错误:
new = nums.sort()得到None。 修正: 使用sorted(nums)。
关键要点
list是可变的;tuple是不可变的,且可哈希。- 推导式比
for+append更快、更清晰。 sorted(x)返回一个新列表;x.sort()就地修改并返回None。key=接受一个函数:key=str.lower、key=len、key=lambda t: t[1]。b = a共享同一个列表;想要独立的列表请用a.copy()。