哈希表:最近最少使用缓存

背景

哈希表的基础

哈希表是一种更复杂更高效的数据结构,包括HashSet和HashMap,他们的查询删除一个元素的时间复杂度都是O(1)。常用来优化时间效率。哈希表虽然高效,但不是万能,如果需要对元素排序,则用TreeMap。

哈希表的设计

  1. 数组: 基于数组设计哈希表,元素的哈希值对数组长度的余数作为数组下标,使得查找元素时间效率为O(1)。
  2. 链表:当哈希值相等时,数组下标就会产生冲突,需要把存入数组的同一位置的多个元素用链表存起来。
  3. 扩容:为了保证高效,当元素数目和数组长度的比值超过阈值时,我们需要对数组长度扩容。

题目

请设计实现一个最近最少使用缓存,要求如下两个操作的时间复杂度都是O(1)。

  • get(key)
  • put(key, value)

解题思路

本题要求我们设计一个缓存的数据结构,

  1. 我们得把节点访问的顺序记录起来。如果我们用HashMap, 使用get,put时,可以实现时间复杂度O(1),每次访问一个节点,我们就把节点的key作为键存入hashMap中,节点最为HashMap的值储存,同时节点加入链表的末尾,这样链表的头部节点时最近最少使用的节点,而尾节点就是最新使用的节点。
  2. 双向链表取代单向链表。 使得能快速删除和查询节点。

需要注意的点:

  1. 节点数目超出阈值时,需要淘汰最近最少使用的节点。
  2. 借助哨兵节点,方便更新缓存。

具体的逻辑代码如下:

代码

class LRUCache {
   private int capacity = 0;
   private ListNode head;
   private ListNode tail;
   private Map<Integer, ListNode> cache;

    public LRUCache(int capacity) {
        this.capacity = capacity;//阈值
        //初始化缓存哈希表
        cache = new HashMap<Integer, ListNode>();
        
        //初始化双向链表, 这里哨兵节点用于快速添加尾部节点。
        head = new ListNode(-1, -1);
        tail = new ListNode(-1, -1);
        head.next = tail;
        tail.pre = head;
    }
    
    public int get(int key) {
        ListNode node = cache.get(key);
        if(node == null) {
            return -1;
        }
        moveToTail(node, node.value);
        return node.value;
    }
    
    public void put(int key, int value) {
        if(cache.containsKey(key)) {
            moveToTail(cache.get(key), value);
        } else {
            if(cache.size() == capacity) {
                ListNode tobeDeleteNode = head.next;
                deleteNode(tobeDeleteNode);
                cache.remove(tobeDeleteNode.key);
            }
             ListNode node = new ListNode(key, value);
             insertToTail(node);
             cache.put(key, node);
        }
    } 

    private void deleteNode(ListNode node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }
    private void moveToTail(ListNode node, int value) {
        //detete
        deleteNode(node);
        //insertToTail
        node.value = value;
        insertToTail(node);
    }

    private void insertToTail(ListNode node) {
        node.pre = tail.pre;
        tail.pre.next  = node;
        node.next = tail;
        tail.pre = node;
    }

    class ListNode {
        ListNode pre;
        ListNode next;
        int key;
        int value;
        public ListNode(int key, int value){
            this.key = key;
            this.value = value;
        }
    }
}

/**
 * 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);
 */

总结

本题考察哈希表的知识点,需要注意最近最少使用缓存需要记录元素访问的顺序,所以最终缓存的数据结构设计成哈希表和双向链表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值