这道题TreeMap的应用场景.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class TimeMap { private Map<Integer, Map<String, String>> store; public TimeMap () { store = new HashMap <>(); } public void set (String key, String value, int timestamp) { if (!store.containsKey(timestamp)) { store.put(timestamp, new HashMap <>()); } store.get(timestamp).put(key, value); } public String get (String key, int timestamp) { if (store.containsKey(timestamp) && store.get(timestamp).containsKey(key)) { return store.get(timestamp).get(key); } for (int i = timestamp - 1 ; i > 0 ; i--) { if (store.containsKey(i) && store.get(i).containsKey(key)) { return store.get(i).get(key); } } return "" ; } }
我的第一版答案. 使用的是双层hashmap.
这道题学习Treemap.
时间复杂度: O(n) n就是某个timestamp的值给进去, 假如没找到, 就挨个往前找, 遍历一遍. 空间复杂度: O(n) n是所有set的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class TimeMap { Map<String, TreeMap<Integer, String>> map; public TimeMap () { map = new HashMap <>(); } public void set (String key, String value, int timestamp) { map.putIfAbsent(key, new TreeMap <>()); map.get(key).put(timestamp, value); } public String get (String key, int timestamp) { if (!map.containsKey(key)) return "" ; String ans = "" ; TreeMap<Integer, String> currentTreeMap = map.get(key); Integer floorKey = currentTreeMap.floorKey(timestamp); if (floorKey != null ) { ans = currentTreeMap.get(floorKey); } return ans; } }
就是红黑树. 每个node存的是键值对构成的entry, 按照key的大小排序(或者自定义排序方法, 使用comparator来说明排序方式). 然后我们可以在时间复杂度为O(logn)的前提下取到某个给定的key对应的value. 比如那个Time Based Key-Value Store那道题. 我想的是有一个hashmap, key是timestamp, value呢是一个hashmap, 这个inner hashmap存的是某个timestamp下的string, string这样的key value pair. 现在的问题就是如果我们想要得到某个string在某个timestamp下对应的value, 那该怎么办呢? 按照我的操作, 就是先看有没有这个timestamp, 有的话再看在该timestamp下有没有该string作为key的KV pair. 没有的话, 从timestamp开始向下遍历(timestamp – 1, timestamp -2…一直找到timestamp是1的情况, 因为最小就是1)外部的map的timestamp, 去寻找每种timestamp的inner map是否包含该string作为key的KV pair. 这样就会很慢.
试着想一下另一种嵌套方式. 用String-String pair的key作为外层map的key, 外层map对应的value呢是一个hashmap, inner hashmap则是timestamp和string-string pair中的value组成的pair. 于是我们给定一个string-string key value pair以及timestamp, 我们首先看这个string key是否存在, 不存在的话, 等于是在任何timestamp下都没有插入过该string-string pair. 如果存在, 我们找到这个key对应的hashmap, 这个map中呢存的是各种timestamp下以该string key对应的value的值. 此时又有一个问题, 如果刚好我们要的timestamp存在那还好, 直接访问到返回即可, 如果没有呢? 就要遍历整个inner map来找到最接近现在timestamp值的那个entry(该entry中的timestamp最接近我们想要的timestamp, 这是题目的要求)了, 因为这个hashmap的entry不是按照timestamp排好序的.
此时TreeMap就派上用场了. TreeMap就能把给定的键值对按照键的大小(键的大小可以根据natural ordering也可以根据我们提供的comparator)来排序并且可以在log(n)时间获得某个key对应的value. 于是如果inner map存在我们想要的timestamp, 那么直接获取, 如果没有, 那么获取此时存有的最大的timestamp即可. 毕竟红黑树就是个BST, 那么获取树中最大的timestamp也就是需要log(n)时间.
除了可以使用TreeMap, 我们还有别的方法. 我们忽略了一个点, 就是这道题有说过timestamp的值是不会递减的. 也就是后插入的timestamp不会比前面小. 也就是对于某个KV pair, 我们先插入K, V1(timestamp1), 后插入K, V2(timestamp2), timestamp2是不会小于timestamp1的. 于是我们的inner map可以换成List. list中存的还是timestamp-string pair, 按照题目的说话, timestamp只会越来越大不会变小, 那么这个list就是sorted了, 因为是先来后到, 对于同一个string key后插入的就会在list的后面. 对于sorted list, 想要查找某个值, binary search就自然而然被想到了. 先使用binary search找我们想要的timestamp, 如果有的话就返回它, 没有的话返回list中最后一个element中的value即可.
需要注意的是第63行的if是必要的, 因为get可以随便, 比如我插入个timestamp是10, 我取5, 如果没这个if, 直接null pointer exception了. 不能直接返回currentTreeMap.get(floorKey), floorKey可能为null.
时间复杂度: O(logn) 空间复杂度: O(n) n是set的个数.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 class TimeMap { static class Data { int timestamp; String value; Data(int timestamp, String value) { this .timestamp = timestamp; this .value = value; } } Map<String, List<Data>> map; public TimeMap () { map = new HashMap <>(); } public void set (String key, String value, int timestamp) { map.putIfAbsent(key, new ArrayList <>()); map.get(key).add(new Data (timestamp, value)); } public String get (String key, int timestamp) { if (!map.containsKey(key)) return "" ; List<Data> list = map.get(key); return binarySearch(list, timestamp); } private String binarySearch (List<Data> list, int timestamp) { int left = 0 ; int right = list.size() - 1 ; while (left < right) { int middle = left + (right - left) / 2 ; if (list.get(middle).timestamp == timestamp) { return list.get(middle).value; } else if (list.get(middle).timestamp < timestamp) { if (list.get(middle + 1 ).timestamp > timestamp) return list.get(middle).value; left = middle + 1 ; } else { right = middle - 1 ; } } return list.get(left).timestamp <= timestamp ? list.get(left).value : "" ; }} String answer = "" ; List<Pair> values = map.get(key); int left = 0 ; int right = values.size() - 1 ; while (left<=right) { int middle = left + (right - left) / 2 ; if (list.get(middle).timestamp == timestamp) { return list.get(middle).value; } else if (list.get(middle).timestamp < timestamp) { result = middle; left = middle + 1 ; } else { right = middle - 1 ; } }