Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted. Follow up:
Could you do both operations in O(1) time complexity? Example: LFUCache cache = new LFUCache( 2 /* capacity */ ); cache.set(1, 1);
cache.set(2, 2);
cache.get(1); // returns 1
cache.set(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.get(3); // returns 3.
cache.set(4, 4); // evicts key 1.
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4

referred to: https://discuss.leetcode.com/topic/69137/java-o-1-accept-solution-using-hashmap-doublelinkedlist-and-linkedhashset

Two HashMaps are used, one to store <key, value> pair, another store the <key, node>.
I use double linked list to keep the frequent of each key. In each double linked list node, keys with the same count are saved using java built in LinkedHashSet. This can keep the order.
Every time, one key is referenced, first find the current node corresponding to the key, If the following node exist and the frequent is larger by one, add key to the keys of the following node, else create a new node and add it following the current node.
All operations are guaranteed to be O(1).

 public class LFUCache {
int cap;
ListNode head;
HashMap<Integer, Integer> valueMap;
HashMap<Integer, ListNode> nodeMap; public LFUCache(int capacity) {
this.cap = capacity;
this.head = null;
this.valueMap = new HashMap<Integer, Integer>();
this.nodeMap = new HashMap<Integer, ListNode>();
} public int get(int key) {
if (valueMap.containsKey(key)) {
increaseCount(key);
return valueMap.get(key);
}
return -1;
} public void set(int key, int value) {
if (cap == 0) return;
if (valueMap.containsKey(key)) {
valueMap.put(key, value);
increaseCount(key);
}
else {
if (valueMap.size() < cap) {
valueMap.put(key, value);
addToHead(key);
}
else {
removeOld();
valueMap.put(key, value);
addToHead(key);
}
} } public void increaseCount(int key) {
ListNode node = nodeMap.get(key);
node.keys.remove(key);
if (node.next == null) {
node.next = new ListNode(node.count+1);
node.next.prev = node;
node.next.keys.add(key);
}
else if (node.next.count == node.count + 1) {
node.next.keys.add(key);
}
else {
ListNode newNode = new ListNode(node.count+1);
newNode.next = node.next;
node.next.prev = newNode;
newNode.prev = node;
node.next = newNode;
node.next.keys.add(key);
}
nodeMap.put(key, node.next);
if (node.keys.size() == 0) remove(node);
} public void remove(ListNode node) {
if (node.next != null) {
node.next.prev = node.prev;
}
if (node.prev != null) {
node.prev.next = node.next;
}
else { // node is head
head = head.next;
}
} public void addToHead(int key) {
if (head == null) {
head = new ListNode(1);
head.keys.add(key);
}
else if (head.count == 1) {
head.keys.add(key);
}
else {
ListNode newHead = new ListNode(1);
head.prev = newHead;
newHead.next = head;
head = newHead;
head.keys.add(key);
}
nodeMap.put(key, head);
} public void removeOld() {
if (head == null) return;
int old = 0;
for (int keyInorder : head.keys) {
old = keyInorder;
break;
}
head.keys.remove(old);
if (head.keys.size() == 0) remove(head);
valueMap.remove(old);
nodeMap.remove(old);
} public class ListNode {
int count;
ListNode prev, next;
LinkedHashSet<Integer> keys;
public ListNode(int freq) {
count = freq;
keys = new LinkedHashSet<Integer>();
prev = next = null;
}
}
} /**
* Your LFUCache object will be instantiated and called as such:
* LFUCache obj = new LFUCache(capacity);
* int param_1 = obj.get(key);
* obj.set(key,value);
*/

Summary of LinkedHashSet: http://www.programcreek.com/2013/03/hashset-vs-treeset-vs-linkedhashset/

A Set contains no duplicate elements. That is one of the major reasons to use a set. There are 3 commonly used implementations of Set: HashSet, TreeSet and LinkedHashSet. When and which to use is an important question. In brief, if you need a fast set, you should use HashSet; if you need a sorted set, then TreeSet should be used; if you need a set that can be store the insertion order, LinkedHashSet should be used.

1. Set Interface

Set interface extends Collection interface. In a set, no duplicates are allowed. Every element in a set must be unique. You can simply add elements to a set, and duplicates will be removed automatically.

2. HashSet vs. TreeSet vs. LinkedHashSet

HashSet is Implemented using a hash table. Elements are not ordered. The add, remove, and contains methods have constant time complexity O(1).

TreeSet is implemented using a tree structure(red-black tree in algorithm book). The elements in a set are sorted, but the add, remove, and contains methods has time complexity of O(log (n)). It offers several methods to deal with the ordered set like first(), last(), headSet(), tailSet(), etc.

LinkedHashSet is between HashSet and TreeSet. It is implemented as a hash table with a linked list running through it, so it provides the order of insertion. The time complexity of basic methods is O(1).

3. TreeSet Example

TreeSet<Integer> tree = new TreeSet<Integer>();
tree.add(12);
tree.add(63);
tree.add(34);
tree.add(45);
 
Iterator<Integer> iterator = tree.iterator();
System.out.print("Tree set data: ");
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}

Output is sorted as follows:

Tree set data: 12 34 45 63 

4. HashSet Example

HashSet<Dog> dset = new HashSet<Dog>();
dset.add(new Dog(2));
dset.add(new Dog(1));
dset.add(new Dog(3));
dset.add(new Dog(5));
dset.add(new Dog(4));
Iterator<Dog> iterator = dset.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}

Output:

5 3 2 1 4

Note the order is not certain.

5. LinkedHashSet Example

LinkedHashSet<Dog> dset = new LinkedHashSet<Dog>();
dset.add(new Dog(2));
dset.add(new Dog(1));
dset.add(new Dog(3));
dset.add(new Dog(5));
dset.add(new Dog(4));
Iterator<Dog> iterator = dset.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}

The order of the output is certain and it is the insertion order:

2 1 3 5 4 

Leetcode: LFU Cache && Summary of various Sets: HashSet, TreeSet, LinkedHashSet的更多相关文章

  1. Set集合[HashSet,TreeSet,LinkedHashSet],Map集合[HashMap,HashTable,TreeMap]

    ------------ Set ------------------- 有序: 根据添加元素顺序判定, 如果输出的结果和添加元素顺序是一样 无序: 根据添加元素顺序判定,如果输出的结果和添加元素的顺 ...

  2. [LeetCode] LFU Cache 最近最不常用页面置换缓存器

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  3. LeetCode LFU Cache

    原题链接在这里:https://leetcode.com/problems/lfu-cache/?tab=Description 题目: Design and implement a data str ...

  4. Java容器---Set: HashSet & TreeSet & LinkedHashSet

    1.Set接口概述        Set 不保存重复的元素(如何判断元素相同呢?).如果你试图将相同对象的多个实例添加到Set中,那么它就会阻止这种重复现象. Set中最常被使用的是测试归属性,你可以 ...

  5. [LeetCode] 460. LFU Cache 最近最不常用页面置换缓存器

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  6. leetcode 146. LRU Cache 、460. LFU Cache

    LRU算法是首先淘汰最长时间未被使用的页面,而LFU是先淘汰一定时间内被访问次数最少的页面,如果存在使用频度相同的多个项目,则移除最近最少使用(Least Recently Used)的项目. LFU ...

  7. [LeetCode] LRU Cache 最近最少使用页面置换缓存器

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  8. LeetCode Monotone Stack Summary 单调栈小结

    话说博主在写Max Chunks To Make Sorted II这篇帖子的解法四时,写到使用单调栈Monotone Stack的解法时,突然脑中触电一般,想起了之前曾经在此贴LeetCode Al ...

  9. LFU Cache

    2018-11-06 20:06:04 LFU(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”. ...

随机推荐

  1. UDP协议开发

    UDP是用户数据报协议(User Datagram Protocol,UDP)的简称,其主要作用是将网络数据流量压缩成数据报形式,提供面向事务的简单信息传送服务.与TCP协议不同,UDP协议直接利用I ...

  2. [工作中的设计模式]解释器模式模式Interpreter

    一.模式解析 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 以上是解释器模式的类图,事实上我 ...

  3. Java基础知识梳理《一》

    一.Java数据类型(简单称之为“四类八种”) java 基本的数据类型长度都是固定的,好处是在实现跨平台时就统一了. 1.整型 byte short int long (分别是1,2,4,8个字节) ...

  4. easyui datalist按组多选

    结果如下: 数据样式如下: [ {"text":"Epson WorkForce 845","group":"Printer&qu ...

  5. Leetcode N-Queens II

    Follow up for N-Queens problem. Now, instead outputting board configurations, return the total numbe ...

  6. CF #374 (Div. 2) D. 贪心,优先队列或set

    1.CF #374 (Div. 2)   D. Maxim and Array 2.总结:按绝对值最小贪心下去即可 3.题意:对n个数进行+x或-x的k次操作,要使操作之后的n个数乘积最小. (1)优 ...

  7. Velocity 语法(转)

    一.基本语法 1."#"用来标识Velocity的脚本语句,包括#set.#if .#else.#end.#foreach.#end.#iinclude.#parse.#macro ...

  8. 李洪强iOS经典面试题141-报错警告调试

    李洪强iOS经典面试题141-报错警告调试   报错警告调试 你在实际开发中,有哪些手机架构与性能调试经验 刚接手公司的旧项目时,模块特别多,而且几乎所有的代码都写在控制器里面,比如UI控件代码.网络 ...

  9. struts2自定义MVC框架

    自定义MVC:(首先了解Model1和Model2的概念) Model1与Model2: Model1:就是一种纯jsp开发技术,将业务逻辑代码和视图渲染代码杂糅在一起. Model2:Model2是 ...

  10. 详解Js中文件读取机制

    前言,文件读取是提高应用体验度的必须接口,应用场景中需求很频繁. Js处理文件读取,由于处于安全方面的考虑,在2000年以前,都是以“<input type="file"&g ...