LeetCode-面试题16.25 LRU缓存机制题解

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:

LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
 

进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

 

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

1.手写双向链表+使用map经典解法

class LRUCache {
    /**
     * 双向链表的节点
     */
    static class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;

        public DLinkedNode() {
        }

        public DLinkedNode(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }

    //lru的缓存
    private final Map cache = new HashMap<>();
    //双向链表的长度
    private int size;
    //lru的容量
    private final int capacity;
    //双向链表的前驱节点指针和后继节点指针
    private final DLinkedNode head;
    private final DLinkedNode tail;


    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        //使用伪头部和伪尾部节点
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.value;
    }

    //获取过的节点移动到链表的头部位置,并删除链表尾部的节点
    private void moveToHead(DLinkedNode node) {
        removeNode(node);
        addToHead(node);
    }

    //节点添加到头部
    private void addToHead(DLinkedNode node) {
        node.prev=head;
        node.next=head.next;
        head.next.prev=node;
        head.next=node;
    }

    private void removeNode(DLinkedNode node) {
        node.prev.next=node.next;
        node.next.prev=node.prev;
    }

    public void put(int key, int value) {
        DLinkedNode node=cache.get(key);
        if (node==null) {
            DLinkedNode newNode = new DLinkedNode(key, value);
            cache.put(key, newNode);
            //新添加的节点放在头部
            addToHead(newNode);
            ++size;
            if(size > capacity){
                DLinkedNode tail=removeTail();
                cache.remove(tail.key);
                --size;
            }
        }else {
            node.value=value;
            moveToHead(node);
        }
    }

    private DLinkedNode removeTail() {
        DLinkedNode res = tail.prev;
        removeNode(res);;
        return res;
    }
}

2.手写哈希表的解法

public class LRUCache {
    private  MyLinkedHashMap map;

    public LRUCache(int capacity) {
        map = new MyLinkedHashMap(capacity);
    }

    public int get(int key) {
        return map.getOrDefault(key, -1);
    }

    public void put(int key, int value) {
        map.put(key, value);
    }

    private class MyLinkedHashMap {

        private final Entry[] table;

        int modCount;

        int size;

        class Entry {
            int hash;
            K key;
            V value;
            Entry next;

            Entry before, after;

            public Entry() {
            }


            public Entry(int hash, K key, V value, Entry node) {
                this.hash = hash;
                this.key = key;
                this.value = value;
                this.next = node;
            }
        }

        Entry head;

        Entry tail;


        public MyLinkedHashMap(int initialCapacity) {
            this.table = new Entry[initialCapacity];
            modCount = initialCapacity;
            size=0;
        }

        final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }

        Entry newNode( K key, V value,Integer hash,Entry next) {
            Entry p= new Entry( hash,key, value,next);
            linkNodeLast(p);
            return p;
        }

        private void linkNodeLast(Entry p){
            Entry last=tail;
            tail=p;
            if(last==null){
                head=p;
            }else{
                p.before=last;
                last.after=p;
            }
        }
        public V get(Object key) {
            Entry e;
            if ((e = getNode(hash(key), key)) == null)
                return null;
                afterNodeAccess(e);
            return e.value;
        }

         Entry getNode(int hash, Object key) {
            Entry[] tab; Entry first, e; int n; K k;
            if ((tab = table) != null && (n = tab.length) > 0 &&
                    (first = tab[(n - 1) & hash]) != null) {
                if (first.hash == hash && // always check first node
                        ((k = first.key) == key || (key != null && key.equals(k))))
                    return first;
                if ((e = first.next) != null) {
                    do {
                        if (e.hash == hash &&
                                ((k = e.key) == key || (key != null && key.equals(k))))
                            return e;
                    } while ((e = e.next) != null);
                }
            }
            return null;
        }

        public V put(K key, V value) {
            Integer hash = hash(key);
            Entry p;
            int n, i;
            n = modCount;
            if ((p = table[i = (n - 1) & hash]) == null) {
                table[i] = newNode(key,value,hash,null);
                size++;
            } else {
                Entry e; K k;
                if (p.hash == hash &&
                        ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;
                else {
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {
                            p.next = newNode(key, value, hash,null);
                            size++;
                            break;
                        }
                        if (e.hash == hash &&
                                ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            afterNodeInsertion();
            return null;
        }

        private void afterNodeInsertion() {
            Entry first;
            if ( (first = head) != null && removeEldestEntry()) {
                K key = first.key;
                removeNode(hash(key), key,null, false, true);
            }
        }

      Entry removeNode(int hash, Object key, Object value,
                                   boolean matchValue, boolean movable) {
          Entry[] tab; Entry p; int n, index;
          if ((tab = table) != null && (n = tab.length) > 0 &&
                  (p = tab[index = (n - 1) & hash]) != null) {
              Entry node = null, e; K k; V v;
              if (p.hash == hash &&
                      ((k = p.key) == key || (key != null && key.equals(k))))
                  node = p;
              else if ((e = p.next) != null) {
                      do {
                          if (e.hash == hash &&
                                  ((k = e.key) == key ||
                                          (key != null && key.equals(k)))) {
                              node = e;
                              break;
                          }
                          p = e;
                      } while ((e = e.next) != null);
              }
              if (node != null && (!matchValue || (v = node.value) == value ||
                      (value != null && value.equals(v)))) {
                   if (node == p)
                      tab[index] = node.next;
                  else
                      p.next = node.next;
                  --size;
                  afterNodeRemoval(node);
                  return node;
              }
          }
          return null;
      }

        //从链表将该元素删除
        private void afterNodeRemoval(Entry e) {
            Entry p =e, b = p.before, a = p.after;
            p.before = p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a == null)
                tail = b;
            else
                a.before = b;
        }
        private boolean removeEldestEntry() {
            return size>modCount;
        }

        private void afterNodeAccess(Entry e) {
            Entry last;
            if ((last = tail) != e) {
                Entry p =e, b = p.before, a = p.after;
                p.after = null;
                if (b == null)
                    head = a;
                else
                    b.after = a;
                if (a != null)
                    a.before = b;
                else
                    last = b;
                if (last == null)
                    head = p;
                else {
                    p.before = last;
                    last.after = p;
                }
                tail = p;
            }
        }


        public V getOrDefault(Object key, V defaultValue) {
            V value;
            if ((value = get(key)) == null) {
                return defaultValue;
            }
            return value;
        }

    }
    public static void main(String[] args) {
        LRUCache cache = new LRUCache(2);
        cache.put(1, 1);
        cache.put(2, 2);

        int res1 = cache.get(1);
        System.out.println(res1);

        cache.put(3, 3);

        int res2 = cache.get(2);
        System.out.println(res2);

        int res3 = cache.get(3);
        System.out.println(res3);

        cache.put(4, 4);
        int res4 = cache.get(1);
        System.out.println(res4);

        int res5 = cache.get(3);
        System.out.println(res5);

        int res6 = cache.get(4);
        System.out.println(res6);
    }
}

 

 

你可能感兴趣的:(Java,数据结构与算法,java,链表,数据结构,hashmap)