1. hash表+暴力扫描
解题思路:时间复杂度insert O(1),sum O(n * m),其中n是key的数量,m是前缀长度。空间复杂度O(n * m) |
---|
- 直接用一个map存储key和val
- sum时,直接遍历map中所有的key值,只要和要找的prefix前缀具有包含关系,就累加
- 所以这个办法,就是纯粹的暴力求解,每sum一个前缀,都需要比对每一个key的每一个字母
class MapSum {
Map<String, Integer> map;
public MapSum() {
map = new HashMap<>();
}
public void insert(String key, int val) {
map.put(key,val);
}
public int sum(String prefix) {
int res = 0;
for (String s : map.keySet()) {
if (s.startsWith(prefix)) {
res += map.get(s);
}
}
return res;
}
}
2. 前缀和映射
和上面不一样的是,这个思路,插入的时间开销多,但是查的时候快。上面的思路是插入快,但是查的慢。
解题思路:时间复杂度insert O(
n
2
n^2
n2),sum O(1),其中n是key的长度。空间复杂度O(n * m) |
---|
- 将每个要插入的key,都将所有可能的前缀进行拆分,例如apple拆为a,ap,app,appl,apple.
- 那么插入这些子前缀时,一定是以累加的形式,就是先获取原来的值,然后将这个累加插入
- 但是此时就有一个问题,如果我们再次插入apple,并且对应val不一样呢?
- 假设这次apple对应的val我们想要改为比上次少5.那么a,ap,app,appl,apple.就都得-5
- 怎么做才能方便的进行这个都-5的操作呢?
- 引入一个delta变量
- 如果key不存在,delta等于val
- 如果key存在,获取已存在key的值,让delta = val - map[key]. 那么就获取这次的val与上次插入的差值了
- 然后对a,ap,app,appl,apple.累加delta即可
class MapSum {
Map<String, Integer> map;
Map<String, Integer> prefixmap;
public MapSum() {
map = new HashMap<>();
prefixmap = new HashMap<>();
}
public void insert(String key, int val) {
int delta = val - map.getOrDefault(key, 0);
map.put(key, val);
for (int i = 1; i <= key.length(); ++i) {
String currprefix = key.substring(0, i);
prefixmap.put(currprefix, prefixmap.getOrDefault(currprefix, 0) + delta);
}
}
public int sum(String prefix) {
return prefixmap.getOrDefault(prefix, 0);
}
}
3. 前缀树(字典树)Trie树
前缀树是专门处理类似这种情况的数据结构
解题思路:时间复杂度insert O(n),sum O(n),其中n是key的长度。空间复杂度O(c * n * m),其中m是键值对数量,c是常数 |
---|
- 利用前缀树的特性,插入时,一层一层按照字符插入即可,查找时也是一样
- 因为这道题依然是所有字符串都是英文字母,所以还是一棵26叉树,代表26个字母
- 通过26个不同的字母,按照不同的顺序和长度组合,理论上可以组合出世界上所有的单词
- 但是这道题,还指定一个val值,这个是前缀树自己做不到的
所以借用方法二的思路,用一个map存储插入的key和val,引入delta变量
- 如果key不存在,delta等于val
- 如果key存在,获取已存在key的值,让delta = val - map[key]. 那么就获取这次的val与上次插入的差值了.
- 这样我们就可以处理前缀树中每个前缀的和。
因为前缀树每个结点保存都是val累加完成之后的结果,现在的情况是,某一次的累加数组val产生了变化,我们不知道是和上次差多少,所以只能用一个map来记录,让我们知道和上次差多少,即时修改val
class MapSum {
class TrieNode {
int val = 0;
TrieNode[] next = new TrieNode[26];
}
TrieNode root;
Map<String, Integer> map;
public MapSum() {
root = new TrieNode();
map = new HashMap<>();
}
public void insert(String key, int val) {
int delta = val - map.getOrDefault(key, 0);
map.put(key, val);
TrieNode node = root;
for (char c : key.toCharArray()) {
if (node.next[c - 'a'] == null) {
node.next[c - 'a'] = new TrieNode();
}
node = node.next[c - 'a'];
node.val += delta;
}
}
public int sum(String prefix) {
TrieNode node = root;
for (char c : prefix.toCharArray()) {
if (node.next[c - 'a'] == null) {
return 0;
}
node = node.next[c - 'a'];
}
return node.val;
}
}