设计数字容器系统
难度:
标签:
题目描述
代码结果
运行时间: 341 ms, 内存: 72.1 MB
/*
* 思路:
* 1. 使用两个映射来实现:一个映射indexToNumber记录每个下标对应的数字;另一个映射numberToIndices记录每个数字对应的所有下标。
* 2. change方法将数字插入或替换到指定下标,并相应更新两个映射。
* 3. find方法返回给定数字的最小下标。
* 4. 使用Java Stream API简化find方法。
*/
import java.util.*;
import java.util.stream.*;
public class NumberContainers {
private Map<Integer, Integer> indexToNumber;
private Map<Integer, TreeSet<Integer>> numberToIndices;
public NumberContainers() {
indexToNumber = new HashMap<>();
numberToIndices = new HashMap<>();
}
public void change(int index, int number) {
if (indexToNumber.containsKey(index)) {
int oldNumber = indexToNumber.get(index);
if (numberToIndices.containsKey(oldNumber)) {
numberToIndices.get(oldNumber).remove(index);
if (numberToIndices.get(oldNumber).isEmpty()) {
numberToIndices.remove(oldNumber);
}
}
}
indexToNumber.put(index, number);
numberToIndices.putIfAbsent(number, new TreeSet<>());
numberToIndices.get(number).add(index);
}
public int find(int number) {
return numberToIndices.getOrDefault(number, new TreeSet<>()).stream().findFirst().orElse(-1);
}
}
解释
方法:
此题解采用了散列表和最小堆的组合来实现数字容器系统。散列表 `m` 用于保存 `index` 和 `number` 的映射,以便在 O(1) 时间内访问或更新任何索引处的数字。另一方面,`ms` 也是一个散列表,但其值是一个最小堆,用于存储每个数字的所有索引。通过最小堆,我们可以快速访问每个数字的最小索引。在 `change` 方法中,每次改变或插入一个数字时,都会更新散列表 `m` 和对应数字的堆。在 `find` 方法中,为了找到某个数字的最小索引,我们检查堆顶元素是否仍然有效(即堆顶索引处的数字与查询的数字匹配),如果不匹配则将堆顶元素弹出,直到堆顶元素有效或堆为空。
时间复杂度:
O(log n) for `change`, O(log n) average for `find`, but can degrade to O(n) in worst case
空间复杂度:
O(I + N) where I is the number of unique indices and N is the total number of operations
代码细节讲解
🦆
为什么选择使用散列表来存储索引到数字的映射?是否有其他数据结构能实现相同的功能?
▷🦆
在`change`方法中,如果同一个索引多次更新为不同的数字,最小堆中是否会存在过期的索引?这会如何影响性能?
▷🦆
题解中提到在`find`操作中可能需要连续弹出多个过时的堆顶元素,为什么不在`change`方法中直接处理这些过时的堆顶元素?
▷🦆
这种设计是否支持并发操作?如果需要支持并发,应该如何修改现有的实现?
▷