布隆过滤器(Bloom Filter)

布隆过滤器 VS HashTable

布隆过滤器和哈希表类似。

哈希表:HashTable + 拉链存储重复元素。

对于哈希表,不仅存在哈希函数来得到 index 值,还要把整个元素全部放到哈希表中。哈希表是一种没有误差的数据结构,且有多少元素,每个元素有多大,所以的元素需要占用的内容空间,在哈希表中都要找相应的内存大小给存起来。

很多时候,在工业级应用中,我们并不需要存所有的元素本身,只需要存储这个元素在表中是否存在。这种情况下,如果只想查询有还是没有,这时我们需要一种更高效的数据结构。

布隆过滤器是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于家检索一个元素是否在一个集合中。

布隆过滤器优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率、删除困难。

示意图

bloom_filter.png

如果一个元素它所对应的二进制位只要一个为 0,就说明这个元素不在布隆过滤器的索引中。
但是当一个元素刚好分配的三个二进制都为 1 时,我们不能肯定说元素存在于布隆过滤器的索引中,只能说是可能存在。

在布隆过滤器中查询元素,如果查不到,说明肯定不在,如果元素在布隆过滤器中二进制位都是 1,只能说它可能存在。

布隆过滤器,只适合在外面当缓存使用,进行快速判断。当元素查到,就会继续在数据库中去查。布隆过滤器可以节省访问数据库时间。

案例

  • 比特币网络
  • 分布式系统(Map-Reduce)- Hadoop、search engine
  • Redis 缓存
  • 垃圾邮件、评论过滤等

实现

LRU Cache

  • 两个要素:大小、替换策略
  • Hash Table + Double LinkedList
  • O(1) 查询、O(1) 修改、更新

LRU: least recent use 最近最少使用

工作示例

LRU_Cache.png

替换策略

  • LFU - least frequently used
    • 统计每个元素被使用的频次最少的,放到最下面,最先被淘汰掉
  • LRU - least recently used
    • 最先淘汰不常使用的

替换算法总览:https://en.wikipedia.org/wiki/Cache_replacement_policies

相关题目

LRU 缓存

  1. // LRU 缓存
  2. // 双向链表 + HashTable
  3. /**
  4. * @param {number} capacity
  5. */
  6. var LRUCache = function(capacity) {
  7. this.capacity = capacity;
  8. this.hash = {};
  9. this.count = 0;
  10. this.dummyHead = new ListNode();
  11. this.dummyTail = new ListNode();
  12. this.dummyHead.next = this.dummyTail;
  13. this.dummyTail.prev = this.dummyHead;
  14. };
  15. /**
  16. * @param {number} key
  17. * @return {number}
  18. */
  19. LRUCache.prototype.get = function(key) {
  20. const node = this.hash[key];
  21. if (node == null) return -1;
  22. this.moveToHead(node);
  23. return node.value;
  24. };
  25. /**
  26. * @param {number} key
  27. * @param {number} value
  28. * @return {void}
  29. */
  30. LRUCache.prototype.put = function(key, value) {
  31. const node = this.hash[key];
  32. if (node == null) {
  33. if (this.count == this.capacity) {
  34. this.remove();
  35. }
  36. const newNode = new ListNode(key, value);
  37. this.hash[key] = newNode;
  38. this.unshift(newNode);
  39. this.count++;
  40. } else {
  41. node.value = value;
  42. this.moveToHead(node);
  43. }
  44. };
  45. /**
  46. * Your LRUCache object will be instantiated and called as such:
  47. * var obj = new LRUCache(capacity)
  48. * var param_1 = obj.get(key)
  49. * obj.put(key,value)
  50. */
  51. function ListNode (key, value) {
  52. this.key = key;
  53. this.value = value;
  54. this.prev = null;
  55. this.next = null;
  56. }
  57. LRUCache.prototype.swap = function (node) {
  58. const temp1 = node.prev,
  59. temp2 = node.next;
  60. temp1.next = temp2;
  61. temp2.prev = temp1;
  62. }
  63. LRUCache.prototype.unshift = function (node) {
  64. node.prev = this.dummyHead;
  65. node.next = this.dummyHead.next;
  66. this.dummyHead.next.prev = node;
  67. this.dummyHead.next = node;
  68. }
  69. LRUCache.prototype.moveToHead = function (node) {
  70. this.swap(node);
  71. this.unshift(node);
  72. }
  73. LRUCache.prototype.remove = function () {
  74. const tail = this.pop();
  75. delete this.hash[tail.key];
  76. this.count--;
  77. }
  78. LRUCache.prototype.pop = function () {
  79. const tail = this.dummyTail.prev;
  80. this.swap(tail);
  81. return tail;
  82. }