【A】1.5 无序集合/映射
有序集合(映射)容器一般基于平衡二叉搜素树实现,无序集合(映射)容器则一般基于哈希表实现。
哈希表,是数组基于某种映射规则(函数)的⼀种扩展:
- 外部表现为
hash_table['key'] = value,直接通过关键字(浮点数,字符串甚至结构体)索引 - 内部实现为
linear_list[hash(key)] = value,依靠哈希函数把复杂信息映射到一个较小的值域,并作为索引
哈希函数设计得越好,数据分布越均匀,碰撞概率越小。理想情况下,哈希表的插入、查询、删除时间复杂度均为\(O(1)\)。所以,当我们遇到了要快速判断一个元素是否出现集合里的问题,就可以考虑哈希法。
| 题目 | 思路 | 备注 |
|---|---|---|
| 242. 有效的字母异位词|383. 赎金信 | 字符统计|hash(char)=char-'a' |
等价于判断两个频次统计表是否相等 |
| 349. 两个数组的交集 | 对集合\(B\)元素,查询是否也存在于\(A\) | 注意不要一边访问一边删除,HashSet |
| 202. 快乐数 | 模拟,查询是否运算陷入无限死循环 | 另一个检测环的方式是快慢指针 |
| 1. 两数之和 | 在\(x\)前,查询是否存在值\(target-x\) | 下标作为附加信息需要存储,HashMap |
| 454. 四数相加 II | 查询\(A+B\)中是否存在\(0-c+d\) | 将一个四重循环拆分为两个二重循环 |
| 874. 模拟行走机器人 | 模拟,查询当前格点是否属于障碍物 | 哈希设计;地图方向数组 |
| 49. 字母异位词分组 | 字母基元 -> 异位词列表 | 分组和哈希冲突“挂链”的相似性;哈希设计 |
146. LRU缓存

图1.5.1 样例
- 双链表:用于按时间顺序保存数据,便于中间删除
- 哈希表:用于利用关键字索引相应结点,便于查询
- 取值
- 如果关键字存在:定位结点,删除结点、头插、返回
node.val - 如果关键字不存在:返回
-1
- 如果关键字存在:定位结点,删除结点、头插、返回
- 添加
- 如果关键字存在:定位结点、删除结点、头插
- 如果关键字不存在:创建结点、创建索引、头插
- 容量已满:删除索引,淘汰尾部结点
灵感来源:AC136.邻值查找。

图1.5.2 实现:双链表+哈希表
点击查看代码
import java.util.HashMap;
class LRUCache {
int capacity;
Node head;
Node tail;
HashMap<Integer, Node> map;
public LRUCache(int capacity) {
this.capacity = capacity;
head = new Node(-1, -1);
tail = new Node(-1, -1);
head.next = tail;
tail.prev = head;
map = new HashMap<>();
}
public int get(int key) {
// 定位结点,若不存在返回 -1
Node node = map.get(key);
if (node == null) return -1;
// 更新使用状态,链表:删除、头插
node.suicide();
node.append(head);
// 哈希:无需修改
return node.val;
}
public void put(int key, int value) {
// 定位结点,更新属性
if (map.containsKey(key)) {
Node node = map.get(key);
node.val = value;
// 更新使用状态,链表:删除、头插
node.suicide();
node.append(head);
return;
}
// 容量已满,淘汰末尾(最久未使用)
if (map.size() == capacity) {
map.remove(tail.prev.key);
tail.prev.suicide();
}
// 创建结点
Node node = new Node(key, value);
// 哈希:创建索引
map.put(key, node);
// 链表:头插
node.append(head);
}
class Node {
int val;
int key;
Node prev;
Node next;
Node(int key, int val) {
this.key = key;
this.val = val;
}
void suicide() {
this.prev.next = this.next;
this.next.prev = this.prev;
}
void append(Node head) {
this.next = head.next;
this.prev = head;
head.next.prev = this;
head.next = this;
}
}
}

浙公网安备 33010602011771号