数据结构 · #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)
not loaded

元组是不可变的,支持解包,并且可以做字典的键:

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)])
not loaded

排序——就地排序与返回新列表:

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)
not loaded

列表方法(必备完整清单)

| 方法 | 作用 | 示例 | | --- | --- | --- | | 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

快速检查

小练习

该做与不该做

深入一点——复杂度速览
  • 索引访问:O(1)
  • 追加:均摊 O(1)
  • 在头部插入/移除:O(n)
  • 成员检测(x in lst):O(n)

常见错误

关键要点