[LeetCode]146.LRU緩存機制
設計和實現一個 LRU(最近最少使用)緩存 數據結構,使它應該支持以下操作: get
和 put
。
get(key)
- 如果密鑰存在於緩存中,則獲取密鑰的值(總是正數),否則返回 -1。put(key, value)
- 如果密鑰不存在,請設置或插入值。當緩存達到其容量時,它應該在插入新項目之前使最近最少使用的項目作廢。
後續:
你是否可以在 O(1) 時間復雜度中進行兩種操作?註:這道題也是2018今日頭條春招面試題。
案例:
LRUCache cache = new LRUCache( 2 /* 容量 */ ); cache.put(1, 1); cache.put(2, 2); cache.get(1); // 返回 1 cache.put(3, 3); // 該操作,會將 key 2 作廢 cache.get(2); // 返回 -1 (結果不存在) cache.put(4, 4); // 該操作,會將 key 1 作廢 cache.get(1); // 返回 -1 (結果不存在) cache.get(3); // 返回 3 cache.get(4); // 返回 4
求解思路:其實這道題並不難,就是找一個合適的數據結構去存儲,每次get之後,要把get到的數提前到最前面,如果沒有get到,則返回-1。put的時候,先查看有沒有相同的key元素,有的話,直接把那個刪掉,否則不做處理。然後判斷當前的元素個數是否小於capacity,小於的話就在最前面添加新元素即可,否則在最前面添加新元素之後,還要把最後面的元素刪掉。
思路簡單,難的是時間復雜度,我最開始直接想的是利用現成的數據結構,就用的是Java中的LinkedList和HashMap,HashMap中存儲的是key和value,LinkedList中存儲的是若幹個map。代碼如下:
packagecom.darrenchan.dp; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; class LRUCache2 { public int capacity; public List<Map<Integer, Integer>> list = new LinkedList<>(); public LRUCache2(int capacity) {this.capacity = capacity; } public int get(int key) { int value = -1; for (Map<Integer, Integer> map : list) { if(map.get(key) != null){ value = map.get(key); list.remove(map); list.add(0, map); break; } } return value; } public void put(int key, int value) { int index = -1; for (Map<Integer, Integer> map : list) { if(map.get(key) != null){ list.remove(map); break; } } int size = list.size(); Map<Integer, Integer> map = new HashMap<>(); map.put(key, value); if(size < capacity){ list.add(0, map); }else{ list.add(0, map); list.remove(capacity); } } public static void main(String[] args) { LRUCache2 lruCache = new LRUCache2(2); System.out.println(lruCache.get(2)); lruCache.put(2, 6); System.out.println(lruCache.get(1)); lruCache.put(1, 5); lruCache.put(1, 2); System.out.println(lruCache.get(1)); System.out.println(lruCache.get(2)); } }
這樣時間復雜度是O(N),因為每次需要for循環,時間超時。看來不能用現成的了,需要自己構造一個數據結構,這裏采用雙向鏈表和HashMap的結構,HashMap中存儲的是key和Node,Node中存儲的是key和value。HashMap能保證查找的時間復雜度是O(1),雙向鏈表保證的是增刪的時間復雜度是O(1),當然用單向鏈表也可以,就是不太方便。代碼如下:
package com.darrenchan.dp; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; class LRUCache3 { public int capacity; public Map<Integer, Node> map; public Node head;//設一個虛擬的頭結點 public Node tail;//設一個虛擬的尾結點 public int size;//鏈表長度 public LRUCache3(int capacity) { this.capacity = capacity; this.map = new HashMap<>(); head = new Node(0, 0); tail = new Node(0, 0); head.pre = null; head.next = tail; tail.pre = head; tail.next = null; } public void removeNode(Node node){ node.pre.next = node.next; node.next.pre = node.pre; } public void addToHead(Node node){ node.next = head.next; node.next.pre = node; node.pre = head; head.next = node; } public int get(int key) { int value = -1; if(map.get(key) != null){ value = map.get(key).value; removeNode(map.get(key)); addToHead(map.get(key)); } return value; } public void put(int key, int value) { if(map.get(key) != null){ removeNode(map.get(key)); map.remove(key); size--; } Node node = new Node(key, value); map.put(key, node); if(size < capacity){ addToHead(node); size++; }else{ Node remove = tail.pre; removeNode(remove); map.remove(remove.key); addToHead(node); } } public static void main(String[] args) { LRUCache3 lruCache = new LRUCache3(2); System.out.println(lruCache.get(2)); lruCache.put(2, 6); System.out.println(lruCache.get(1)); lruCache.put(1, 5); lruCache.put(1, 2); System.out.println(lruCache.get(1)); System.out.println(lruCache.get(2)); } } class Node{ int key; int value; public Node(int key, int value) { super(); this.key = key; this.value = value; } Node pre; Node next; }
原題鏈接:https://leetcode-cn.com/problems/lru-cache/description/
[LeetCode]146.LRU緩存機制