[Leedcode][JAVA][第460题][LFU]
生活随笔
收集整理的這篇文章主要介紹了
[Leedcode][JAVA][第460题][LFU]
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
【問(wèn)題描述】
設(shè)計(jì)并實(shí)現(xiàn)最不經(jīng)常使用(LFU)緩存的數(shù)據(jù)結(jié)構(gòu)。它應(yīng)該支持以下操作:get?和?put。get(key)?- 如果鍵存在于緩存中,則獲取鍵的值(總是正數(shù)),否則返回 -1。 put(key, value)?- 如果鍵不存在,請(qǐng)?jiān)O(shè)置或插入值。當(dāng)緩存達(dá)到其容量時(shí),它應(yīng)該在插入新項(xiàng)目之前,使最不經(jīng)常使用的項(xiàng)目無(wú)效。在此問(wèn)題中,當(dāng)存在平局(即兩個(gè)或更多個(gè)鍵具有相同使用頻率)時(shí),最近最少使用的鍵將被去除。進(jìn)階: 你是否可以在?O(1)?時(shí)間復(fù)雜度內(nèi)執(zhí)行兩項(xiàng)操作?示例:LFUCache cache = new LFUCache( 2 /* capacity (緩存容量) */ );cache.put(1, 1); cache.put(2, 2); cache.get(1); // 返回 1 cache.put(3, 3); // 去除 key 2 cache.get(2); // 返回 -1 (未找到key 2) cache.get(3); // 返回 3 cache.put(4, 4); // 去除 key 1 cache.get(1); // 返回 -1 (未找到 key 1) cache.get(3); // 返回 3 cache.get(4); // 返回 4來(lái)源:力扣(LeetCode) 鏈接:https://leetcode-cn.com/problems/lfu-cache【解答思路】
1. 核心思想:先考慮訪問(wèn)次數(shù),在訪問(wèn)次數(shù)相同的情況下,再考慮緩存的時(shí)間
- 每次訪問(wèn)一個(gè)已經(jīng)存在的元素的時(shí)候:應(yīng)該先把結(jié)點(diǎn)類(lèi)從當(dāng)前所屬的訪問(wèn)次數(shù)雙鏈表里刪除,然后再添加到它「下一個(gè)訪問(wèn)次數(shù)」的雙向鏈表的頭部。
時(shí)間復(fù)雜度:O(1) 空間復(fù)雜度:O(N)
import java.util.HashMap; import java.util.Map;public class LFUCache {/*** key 就是題目中的 key* value 是結(jié)點(diǎn)類(lèi)*/private Map<Integer, ListNode> map;/*** 訪問(wèn)次數(shù)哈希表,使用 ListNode[] 也可以,不過(guò)要占用很多空間*/private Map<Integer, DoubleLinkedList> frequentMap;/*** 外部傳入的容量大小*/private Integer capacity;/*** 全局最高訪問(wèn)次數(shù),刪除最少使用訪問(wèn)次數(shù)的結(jié)點(diǎn)時(shí)會(huì)用到(這個(gè)設(shè)計(jì)可能是冗余的)*/private Integer maxFrequent = 1;public LFUCache(int capacity) {map = new HashMap<>(capacity);frequentMap = new HashMap<>();this.capacity = capacity;}/*** get 一次操作,訪問(wèn)次數(shù)就增加 1;* 從原來(lái)的鏈表調(diào)整到訪問(wèn)次數(shù)更高的鏈表的表頭** @param key* @return*/public int get(int key) {// 測(cè)試測(cè)出來(lái)的,capacity 可能傳 0if (capacity == 0) {return -1;}if (map.containsKey(key)) {// 獲得結(jié)點(diǎn)類(lèi)ListNode listNode = removeListNode(key);// 掛接到新的訪問(wèn)次數(shù)的雙向鏈表的頭部int frequent = listNode.frequent;addListNode2Head(frequent, listNode);return listNode.value;} else {return -1;}}/*** @param key* @param value*/public void put(int key, int value) {// 如果 key 存在,就更新訪問(wèn)次數(shù) + 1,更新值if (map.containsKey(key)) {ListNode listNode = removeListNode(key);// 更新 valuelistNode.value = value;int frequent = listNode.frequent;addListNode2Head(frequent, listNode);return;}// 如果 key 不存在// 1、如果滿了,先刪除訪問(wèn)次數(shù)最小的的末尾結(jié)點(diǎn),再刪除 map 里對(duì)應(yīng)的 keyif (map.size() == capacity) {for (int i = 1; i <= maxFrequent; i++) {if (frequentMap.containsKey(i) && frequentMap.get(i).count > 0) {// 1、從雙鏈表里刪除結(jié)點(diǎn)DoubleLinkedList doubleLinkedList = frequentMap.get(i);ListNode removeNode = doubleLinkedList.removeTail();// 2、刪除 map 里對(duì)應(yīng)的 keymap.remove(removeNode.key);break;}}}// 2、再創(chuàng)建新結(jié)點(diǎn)放在訪問(wèn)次數(shù)為 1 的雙向鏈表的前面ListNode newListNode = new ListNode(key, value);addListNode2Head(1, newListNode);map.put(key, newListNode);}// 以下部分主要是結(jié)點(diǎn)類(lèi)和雙向鏈表的操作/*** 結(jié)點(diǎn)類(lèi),是雙向鏈表的組成部分*/private class ListNode {private int key;private int value;private int frequent = 1;private ListNode pre;private ListNode next;public ListNode() {}public ListNode(int key, int value) {this.key = key;this.value = value;}}/*** 雙向鏈表*/private class DoubleLinkedList {/*** 虛擬頭結(jié)點(diǎn),它無(wú)前驅(qū)結(jié)點(diǎn)*/private ListNode dummyHead;/*** 虛擬尾結(jié)點(diǎn),它無(wú)后繼結(jié)點(diǎn)*/private ListNode dummyTail;/*** 當(dāng)前雙向鏈表的有效結(jié)點(diǎn)數(shù)*/private int count;public DoubleLinkedList() {this.dummyHead = new ListNode(-1, -1);this.dummyTail = new ListNode(-1, -1);dummyHead.next = dummyTail;dummyTail.pre = dummyHead;count = 0;}/*** 把一個(gè)結(jié)點(diǎn)類(lèi)添加到雙向鏈表的開(kāi)頭(頭部是最新使用數(shù)據(jù))** @param addNode*/public void addNode2Head(ListNode addNode) {ListNode oldHead = dummyHead.next;// 兩側(cè)結(jié)點(diǎn)指向它dummyHead.next = addNode;oldHead.pre = addNode;// 它的前驅(qū)和后繼指向兩側(cè)結(jié)點(diǎn)addNode.pre = dummyHead;addNode.next = oldHead;count++;}/*** 把雙向鏈表的末尾結(jié)點(diǎn)刪除(尾部是最舊的數(shù)據(jù),在緩存滿的時(shí)候淘汰)** @return*/public ListNode removeTail() {ListNode oldTail = dummyTail.pre;ListNode newTail = oldTail.pre;// 兩側(cè)結(jié)點(diǎn)建立連接newTail.next = dummyTail;dummyTail.pre = newTail;// 它的兩個(gè)屬性切斷連接oldTail.pre = null;oldTail.next = null;count--;return oldTail;}}/*** 將原來(lái)訪問(wèn)次數(shù)的結(jié)點(diǎn),從雙向鏈表里脫離出來(lái)** @param key* @return*/private ListNode removeListNode(int key) {// 獲得結(jié)點(diǎn)類(lèi)ListNode deleteNode = map.get(key);ListNode preNode = deleteNode.pre;ListNode nextNode = deleteNode.next;// 兩側(cè)結(jié)點(diǎn)建立連接preNode.next = nextNode;nextNode.pre = preNode;// 刪除去原來(lái)兩側(cè)結(jié)點(diǎn)的連接deleteNode.pre = null;deleteNode.next = null;// 維護(hù)雙鏈表結(jié)點(diǎn)數(shù)frequentMap.get(deleteNode.frequent).count--;// 訪問(wèn)次數(shù)加 1deleteNode.frequent++;maxFrequent = Math.max(maxFrequent, deleteNode.frequent);return deleteNode;}/*** 把結(jié)點(diǎn)放在對(duì)應(yīng)訪問(wèn)次數(shù)的雙向鏈表的頭部** @param frequent* @param addNode*/private void addListNode2Head(int frequent, ListNode addNode) {DoubleLinkedList doubleLinkedList;// 如果不存在,就初始化if (frequentMap.containsKey(frequent)) {doubleLinkedList = frequentMap.get(frequent);} else {doubleLinkedList = new DoubleLinkedList();}// 添加到 DoubleLinkedList 的表頭doubleLinkedList.addNode2Head(addNode);frequentMap.put(frequent, doubleLinkedList);} }作者:liweiwei1419 鏈接:https://leetcode-cn.com/problems/lfu-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-java-by-liweiwei/測(cè)試用例
public class LFUCache {public static void main(String[] args) {LFUCache cache = new LFUCache(3);cache.put(1, 1);cache.put(2, 2);cache.put(3, 3);System.out.println(cache.map.keySet());cache.put(4, 4);System.out.println(cache.map.keySet());int res1 = cache.get(4);System.out.println(res1);int res2 = cache.get(3);System.out.println(res2);int res3 = cache.get(2);System.out.println(res3);int res4 = cache.get(1);System.out.println(res4);cache.put(5, 5);int res5 = cache.get(1);System.out.println(res5);int res6 = cache.get(2);System.out.println(res6);int res7 = cache.get(3);System.out.println(res7);int res8 = cache.get(4);System.out.println(res8);int res9 = cache.get(5);System.out.println(res9);} }【總結(jié)】
1.LFU (Least Frequently Used)緩存機(jī)制(看訪問(wèn)次數(shù))
-在緩存滿的時(shí)候,刪除緩存里使用次數(shù)最少的元素,然后在緩存中放入新元素;
-數(shù)據(jù)的訪問(wèn)次數(shù)很重要,訪問(wèn)次數(shù)越多,就越不容易被刪除;
-根據(jù)題意,「當(dāng)存在平局(即兩個(gè)或更多個(gè)鍵具有相同使用頻率)時(shí),最近最少使用的鍵將被去除」,即在「訪問(wèn)次數(shù)」相同的情況下,按照時(shí)間順序,先刪除在緩存里時(shí)間最久的數(shù)據(jù)
2.編碼總結(jié)
3.功力仍未夠深厚 只能理解其思想
文章轉(zhuǎn)載鏈接: https://leetcode-cn.com/problems/lfu-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-java-by-liweiwei/
總結(jié)
以上是生活随笔為你收集整理的[Leedcode][JAVA][第460题][LFU]的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 酒店业解决方案
- 下一篇: keil c51v952详细安装教程