Lintcode 24 LFU缓存

LFU是一个著名的缓存算法

实现LFU中的set 和 get

样例

capacity = 3

set(2,2)
set(1,1)
get(2)
>> 2
get(1)
>> 1
get(2)
>> 2
set(3,3)
set(4,4)
get(3)
>> -1
get(2)
>> 2
get(1)
>> 1
get(4)
>> 4

思路:利用List构建小顶堆,每次向缓存中添加内容时,调整堆的排序,使最近最访问次数多的在堆底,最近访问次数少的在堆顶。
1.添加元素时,如果key之前没访问过,访问次数为0,获取系统时间,否则访问次数+1
2.当缓存满时删除堆顶元素,向堆尾添加新的元素,向上调整堆。

3.获取元素,访问次数+1,获取系统时间



代码:


public class LFUCache {
    private List<Node> cache = null;  
//存放key在堆中的位置索引,如addr.get(2)=3,即key==2的元素在堆的索引为3
private Map<Integer, Integer> addr = new HashMap<Integer, Integer>();
    private int capacity = 3;  
  
    public LFUCache(int capacity) {  
        cache = new ArrayList<Node>();  
        this.capacity = capacity;  
    }  
  
    public int get(int key) {  
    Integer node_index = addr.get(key); 
        Node node = null;  
        if (node_index == null) {  
            return -1;  
        }  
        node =cache.get(node_index);
        node.accessCount++;  
        node.lastGetTime = System.nanoTime();  
        fixDown(node_index);
        return node.value;  
    }  
  
    public void set(int key, int value) { 
    Integer node_index = addr.get(key); 
    Node node = null;
    if (node_index != null) {  
   
    node = cache.get(node_index);
       
    node.accessCount ++;  
    node.lastGetTime = System.nanoTime();  
    node.value = value;  
    fixDown(node_index);
            return;  
        }else{  
            node = new Node();  
            node.value = value;  
            node.accessCount = 0;  
            node.key = key;
            node.lastGetTime = System.nanoTime();  
            if(this.capacity == 0)return;  
            while (cache.size() >= this.capacity)   
                removeLast();  
            cache.add(node); 
            addr.put(key, cache.size()-1);
            fixUp(cache.size()-1);
        }
    }  
    
    /**
     * 向上调整,小值往上走,用于增加,往上调整不需要制定最上面的索引,肯定是0  
     */
    public void fixUp(int i){
        int father = ( i - 1 )/2;
        
    //当前节点比父节点小进行置换
    while (i>0) {
        if(cache.get(father).compareTo(cache.get(i))<=0){
    break;
    }else {
    swap(father, i);
    i = father;
    father = (i-1)/2;
    }
    }
    }
    

/**
     *向下调整,顶端的大值往下调,主要用于删除和建堆,i表示要调整的节点索引,
     * 删除时候,i是0,建堆时候i从最后一个节点的
     * 父节点依次往下调整  
     */
    public void fixDown(int i){
    int son = 0;
        while (2*i+1<cache.size()){
        //孩子节点
son = 2 * i +1;
//确保son是左右孩子中的最小者
if(son+1<cache.size()&&cache.get(son+1).compareTo(cache.get(son))<0)
son++;
//父节点大于孩子节点进行交换
if (cache.get(i).compareTo(cache.get(son))>0) {
swap(i, son);
i = son;

}else {
break;
}

}
      
    }
    
    /**
     * 交换
     * @param i
     * @param j
     */
    private void swap(int i ,int j){
    Node temp = cache.get(i);
    cache.set(i, cache.get(j));
    cache.set(j, temp);
   
    int ki= cache.get(i).key;
    int kj = cache.get(j).key;
   
    addr.put(ki, i);
    addr.put(kj, j);
    }
  
    /**
     *  淘汰最少使用的缓存  
     */
    private void removeLast() { 
    //底层代码是采用迭代器逐一比较
    Node node = cache.get(0);
    int key = node.key;
    remove(key);
  
    }  
    
    /**
     * 删除堆中和Map中key的元素
     * @param key
     */
    public void remove(Integer key){
   
    Integer pos = addr.get(key);
    System.out.println(pos+"删除的位置");
    if (pos == null) 
    return;
    //将堆尾的元素放到pos位置
    cache.set(pos, cache.get(cache.size()-1));
    //记录堆中pos位置的元素在堆中位置
    addr.put(cache.get(pos).key, pos);
    //移除堆中最后一个元素
    cache.remove(cache.size()-1);
    //对进行调整
    fixDown(pos);
    //Map中也删除对应的key
    addr.remove(key);
    }
    public class Node implements Comparable<Node>{
    private Integer key;
        public Integer value;  
        public Integer accessCount;  //访问次数
        public Long lastGetTime; //上次访问时间


        //淘汰最近最少不访问的,访问次数不等时,返回比较结果,相等时比较访问时间
public int compareTo(Node o) {
int result = accessCount.compareTo(o.accessCount);
return result!=0?result:lastGetTime.compareTo(o.lastGetTime);
}  
        
    }  
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值