哈希表与有序表(HashMap / TreeMap)

哈希表与有序表的能力边界、复杂度、去重语义,以及比较器要点。

#resource / algorithm #type / concept #status / growing #ds / hash #ds / ordered-map

[!info] related notes 算法面试题型 MOC

哈希表与有序表(HashMap / TreeMap)

哈希函数

将任意大小的 key 映射到固定范围的整数(桶下标)。好的哈希函数应当:

  • 确定性:相同输入始终相同输出
  • 均匀性:输出在桶间均匀分布
  • 高效:计算速度快
index = hash(key) % table_size

碰撞处理

链地址法(Chaining)

每个桶维护一个链表(或红黑树)。Java HashMap 在链表长度 > 8 时转为红黑树。

桶0: -> [key1] -> [key5]
桶1: (空)
桶2: -> [key3] -> [key7] -> [key9]

开放寻址法(Open Addressing)

碰撞时按探测序列寻找下一个空槽。常见策略:线性探测、二次探测、双重哈希。

  • 优点:缓存友好(数据连续存储)
  • 缺点:需要控制装载因子,删除麻烦(需要墓碑标记)

装载因子与再哈希

$$\alpha = \frac{n}{m} \quad (n=\text{元素数}, m=\text{桶数})$$

  • 当 $\alpha$ 超过阈值(Java HashMap 默认 0.75),触发再哈希(rehash):桶数翻倍,所有元素重新分布。
  • 再哈希是 $O(n)$ 的摊销操作,但均摊后插入仍为 $O(1)$。

JS 中的 Map / Set

const map = new Map();
map.set('name', 'Alice');
map.get('name');       // 'Alice'
map.has('name');       // true
map.delete('name');
map.size;              // 0

// 遍历保持插入顺序
for (const [key, val] of map) { /* ... */ }

const set = new Set([1, 2, 3, 2]);
// set = {1, 2, 3}  自动去重

JS 的 Map 保持插入顺序,但不提供有序表的能力(不能按 key 大小遍历)。

哈希表 vs 有序表

维度HashMap / HashSetTreeMap / TreeSet
底层实现数组 + 链表/红黑树红黑树
增删改查平均 $O(1)$$O(\log n)$
顺序无序key 有序
区间查询不支持支持(floor/ceiling/subMap)
前驱后继不支持$O(\log n)$
适用场景快速查找、计数、去重需要顺序、区间、排名

去重与比较器

  • Set/Map 的”相等”依赖其比较策略。
  • TreeSet/TreeMap 如果比较器认为两个元素相等,会发生去重。

比较器要点(Java)

  • Comparator<T> 定义元素的排序与”相等”判定(compare(a,b) == 0 视作相等)。
  • 如果你需要”看起来相等但不去重”,通常意味着:
    • 不能用 TreeSet/TreeMap 的默认语义;
    • 或者比较器需要把更多字段纳入比较。

选择建议

  • 只要快:优先哈希表。
  • 需要顺序/区间:用有序表。

相关笔记

创建于 2026/3/16 更新于 2026/5/27