146. LRU缓存机制

题目分析

添加链接描述

有get和put,这是一个键值操作,第一反应是使用map,但是题目有限定条件:首先键值对的大小是有限的,当put键值对的数量超过最大长度时,需要舍弃一些键值对,即那些不常用的键值对。

何为不常用的键值对?
题目给出的描述为最近最少使用的键值对是不常用键值对,也就是说最近进行put,get操作的键值最不会被淘汰,这样就产生了一个类似队列或者链表的模型:
1)每次进行put操作时,如果该值存在于队列之中,就将其拿出并压入队首,如果不存在,则淘汰队尾元素,将该元素插入队头
2)每次进行get操作时,如果该值存在于队列之中,将其放入队首,并返回value,反则返回-1

显然,实现2)中的操作后1)中的部分操作可由2)完成。

实现思路

方法一:
题目要求,所有操作在O(1)的时间完成,只能使用HashMap,而按照题目分析,我们应该有一个类似链表的模型来管理HashMap,这里就可以使用将两者结合,使用HashMap通过key来查询每个单元的地址,双向链表模型来管理每个单元的位置,这样就可以实现全方位O(1)了。
单元应当定义为

class Node{
	int key;
	int value;
	Node pre;
	Node next;
	Node(int key,int value){
		this.key = key;
		this.value = value;
		pre = null;
		next = null;
	}
}

方法二:
直接使用LinkedHashMap

代码和注释


class Node{
	int key;
	int value;
    Node pre;
    Node next;
    static int length = 0;
	Node(int key,int value){
		this.key = key;
		this.value = value;
        this.pre = null;
        this.next = null;
	}
}

class LRUCache {

    Map<Integer, Node> map = new HashMap<>();
    private int capacity;

    private Node dummyNode = new Node(-1,0);
    private Node head = null;
    private Node tail = null;

    public LRUCache(int capacity) {

        //这个很关键
        Node.length = 0;
        this.capacity = capacity;
    }
    public int get(int key) {
        
        if(map.containsKey(key)){
            Node node = map.get(key);
            //如果该点是尾结点,不需要做任何处理
           if(node.key == tail.key){
               return tail.value;
           }
           

           //否则进行链表操作,将该点挪到尾部
           node.pre.next = node.next;
           node.next.pre = node.pre;
           tail.next = node;
           node.pre = tail;
           tail = node;

           //head可能也更改了
           head = dummyNode.next;

        //    if(head!=null&&key == 1){
        //     System.out.println("head   "+head.key+"  tail  "+tail.key);
        // }
           return tail.value;
        }
        return -1;
    }
    
    public void put(int key, int value) {
        //如果链表中存在key,get操作已经将该值挪到尾部
        if(this.get(key)!=-1){
            tail.value = value;
            return;
        }
        //将其加入尾之后
        Node cur = new Node(key,value);

        //如果链表中不存在,压入HashMap中
        map.put(key,cur);
        //先判断是否达到容量上限
        //达到上限,去头,并在map中将其移除,加尾
        if(cur.length==capacity){
            map.remove(head.key);
            
            dummyNode.next = head.next;
            if(head.next!=null){
                //去头
                head.next.pre = dummyNode;
                head = head.next;
                //加尾
                tail.next = cur;
                cur.pre = tail;
                tail = tail.next;
            }
            else{
                dummyNode.next = cur;
                cur.pre = dummyNode;
                head = cur;
                tail = cur;
            }
            head = dummyNode.next;
            return;
        }

        //如果尾部为空,表示插入第一个元素
        if(tail==null){
            dummyNode.next = cur;
            cur.pre = dummyNode;
            head=cur;
            tail=cur;
        }
        //否则,就接上去
        else{
            tail.next = cur;
            cur.pre = tail;
            tail = cur;
        }
        head = dummyNode.next;
        //接进去了一个,自增
        cur.length++;
    }
}


/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

注意事项

1)因为链表的特殊性,如果定义了哑结点、头指针和尾指针,在链表发生结构变化的时候要顶住这几个点,考虑各种情况
2)由于Node中的length是静态变量,在每次LRU初始化的时候都要进行回零操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值