数据结构 · #8 / 11

字典

键值映射、方法与内部机制

为什么重要

哈希表能以平均 O(1) 的时间解决一大类问题。

核心思想

dict 是一张快速查找表:键 → 值。键必须是可哈希的(所以:字符串、数字、元组可以——列表不行)。从 Python 3.7 起,字典会记住插入顺序。

试一试

构建、查找、默认值:

prices = {"apple": 1.0, "banana": 0.5}
prices["cherry"] = 2.5
print(prices)

# .get() returns a default instead of raising KeyError
print(prices.get("grape", 0.0))

# setdefault() inserts if missing, then returns the value
prices.setdefault("date", 3.0)
print(prices)
not loaded

计数——经典的"频率"惯用法:

from collections import Counter

text = "mississippi"
counts = Counter(text)
print(counts)
print(counts.most_common(2))
not loaded

迭代:items() 产出 (key, value) 元组——非常适合解包。

scores = {"Ada": 95, "Linus": 88, "Margaret": 99}
for name, score in scores.items():
  print(f"{name:>10}: {score}")

# Dict comprehension
inverted = { score: name for name, score in scores.items() }
print(inverted)
not loaded

核心字典方法(必须掌握)

| 方法 | 作用 | 示例 | | --- | --- | --- | | get(k, d) | 安全查找 | d.get("x", 0) | | keys() | 键的视图 | list(d.keys()) | | values() | 值的视图 | list(d.values()) | | items() | 键值对的视图 | list(d.items()) | | update(...) | 合并/更新 | d.update({"x":1}) | | pop(k) | 移除某个键 | d.pop("x") | | popitem() | 移除最后一个键值对 | d.popitem() | | setdefault(k, d) | 取值或设置 | d.setdefault("x", 0) | | copy() | 浅拷贝 | d.copy() | | fromkeys(keys, v) | 初始化 | dict.fromkeys(keys, 0) |

分组模式

setdefault 让"按某个键把元素分桶"变成一行代码:

groups = {}
for item in items:
    groups.setdefault(item[0], []).append(item)

两数之和(Two Sum)模式

经典的"记住你见过什么"哈希技巧——O(n) 而非 O(n²):

def two_sum(nums, target):
    seen = {}
    for i, x in enumerate(nums):
        need = target - x
        if need in seen:
            return [seen[need], i]
        seen[x] = i

快速检查

小练习

该做与不该做

深入一点——字典是如何工作的

哈希表内部机制

  • 哈希表: 键被哈希成表中的一个索引。
  • 冲突: 不同的键可能映射到同一个索引。
  • 解决方式: CPython 使用带探测的开放寻址(平均很快)。
  • 负载因子: 随着表被填满,Python 会重新调整其大小以保持查找的快速。

可哈希性规则

  • 可哈希:intfloatstrtuple(如果其元素都可哈希)、frozenset
  • 不可哈希:listdictset

顺序保证

  • Python 3.7+ 保留插入顺序(这是官方的语言保证)。

常见错误

关键要点