1. 程式人生 > >[Swift]LeetCode460. LFU緩存 | LFU Cache

[Swift]LeetCode460. LFU緩存 | LFU Cache

false 上下 true imp keys 忘記 原來 least sel

Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LFUCache cache = new LFUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.get(3);       // returns 3.
cache.put(4, 4);    // evicts key 1.
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

設計並實現最不經常使用(LFU)緩存的數據結構。它應該支持以下操作:getput

get(key) - 如果鍵存在於緩存中,則獲取鍵的值(總是正數),否則返回 -1。
put(key, value) - 如果鍵不存在,請設置或插入值。當緩存達到其容量時,它應該在插入新項目之前,使最不經常使用的項目無效。在此問題中,當存在平局(即兩個或更多個鍵具有相同使用頻率)時,最近最少使用的鍵將被去除。

進階:
你是否可以在 O(1) 時間復雜度內執行兩項操作?

示例:

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

未完善:
  1 import Foundation
  2 class LFUCache {
  3     //最大容量
  4     var capacity:Int
  5     //當前容量
  6     var size:Int
  7     // key 對應的node   node是在小鏈表上面的
  8     var kNMap:[Int:Node]
  9     //Node對應的NodeList的頭是哪個  就是任何一個小結點都能查到在大鏈表的哪個小鏈表上
 10     //一個鏈表對應的大鏈表的結點是哪個
 11     var heads:[Node:NodeList]
 12     //整個大鏈表的頭部  動態的頭部  不一定就是1作為頭
 13     var headList:NodeList?
 14     
 15     init(_ capacity: Int) {
 16         self.capacity = capacity
 17         self.size = 0
 18         kNMap = [Int:Node]()
 19         heads = [Node:NodeList]()
 20         headList = nil
 21     }
 22     
 23     func get(_ key: Int) -> Int {
 24         //特判一下
 25         if capacity == 0 {return -1}
 26         if kNMap[key] == nil {return -1}
 27         //獲取結點所在的原來的鏈表
 28         if let node:Node = kNMap[key]
 29         {
 30             node.times += 1
 31             //找到這個結點屬於的小鏈表
 32             if let curNodeList:NodeList = heads[node]
 33             {
 34                 move(node, curNodeList)
 35             }
 36             //返回對應的node的值
 37             return node.val
 38         }
 39         return -1
 40     }
 41     
 42     func put(_ key: Int, _ val: Int) {
 43         //註意特判
 44         if capacity == 0 {return}
 45         //如果已經存在  就要更新值
 46         if kNMap[key] != nil
 47         {
 48             let node:Node = kNMap[key]!
 49             node.val = val
 50             node.times += 1
 51             //找到屬於哪一個大鏈表
 52             
 53             if let curNodeList = heads[node]
 54             {
 55                 /**
 56                  * move方法
 57                  * 就是在一個大鏈表中,和自己的上下級解耦,然後放到下一個詞頻鏈表中
 58                  * 比如說現在是5 times鏈上的,則從5times鏈中拿出來,如果6存在,放到6times鏈的頭部(頭插法)
 59                  * 如果6不存在,建出6times的鏈表
 60                  */
 61                 move(node, curNodeList)
 62             }
 63         }
 64             //kNMap中不存在,是新插入的  沒包含
 65         else
 66         {
 67             //要先判斷容量夠不夠,已經滿了  要刪掉一個結點
 68             if size == capacity
 69             {
 70                 //要刪掉的就是作為大鏈表的 頭部的尾結點  (次數最少的 用了最久的)
 71                 if let deNode = headList?.tail
 72                 {
 73                     headList?.deleteNode(deNode)
 74                     /**
 75                      * 如果我刪掉了  這個deNode  有可能我整個大鏈表的headList都沒有東西了,整個大Node要刪掉,要更新大headList
 76                      * 又因為加入了新節點,所以又要更新headList
 77                      * 先刪再加
 78                      */
 79                     if headList != nil
 80                     {
 81                         modifyHeadList(headList!)
 82                     }
 83                     //不要忘記在kNMap中刪掉
 84                     kNMap[deNode.key] = nil
 85                     heads[deNode] = nil
 86                     size -= 1
 87                 }
 88             }
 89             //新建  次數為1
 90             let node = Node(key, val, 1)
 91             
 92             //整個大鏈表都不存在
 93             if headList == nil
 94             {
 95                 //建出大鏈表的頭部
 96                 headList = NodeList(node)
 97             }
 98             else
 99             {
100                 //  已經有了大鏈表的頭部
101                 /**
102                  * 如果有 次數為1的頭  就直接添加到大頭的頭部
103                  * 如果沒有次數為1大頭  就建一個大頭   然後添加到大頭的尾部
104                  */
105                 //大鏈表的頭的頭 的次數是 1 也就是說 有為times的小鏈表
106                 if headList?.head?.times == 1
107                 {
108                     //加到這裏
109                     headList?.addNodeFromHead(node)
110                 }
111                 else
112                 {
113                     //沒有times為 1 的小鏈表  要自己建一個
114                     //建出一個times為1的小鏈表
115                     let newList = NodeList(node)
116                     newList.next = headList
117                     headList?.pre = newList
118                     headList = newList
119                 }
120             }
121             //最後再添加這條記錄
122             kNMap[key] = node
123             heads[node] = headList
124             size += 1
125         }
126     }
127     
128     /**
129      * 解耦原來的鏈表  並放入到一個新的鏈表中
130      *
131      * @param node        這個node
132      * @param oldNodeList node 的原來屬於的list
133      */
134     func move(_ node:Node?,_ oldNodeList:NodeList)
135     {
136         //老鏈表你自己先刪掉
137         oldNodeList.deleteNode(node)
138         
139         let preList = modifyHeadList(oldNodeList) ? oldNodeList.pre : oldNodeList
140         //要去的地方
141         let nextList = oldNodeList.next
142         
143         //你的oldNodeList是大鏈表的最後一個
144         if nextList == nil
145         {
146             //建一個
147             let newList = NodeList(node)
148             if preList != nil
149             {
150                 preList?.next = newList
151             }
152             newList.pre = preList
153             if headList == nil
154             {
155                 headList = newList
156             }
157             if node != nil
158             {
159                 heads[node!] = newList
160             }
161         }
162             //不是最後一個不是times最高的
163         else
164         {
165             //下一個存在  就直接掛在下一個的頭部
166             if nextList?.head?.times == node?.times
167             {
168                 nextList?.addNodeFromHead(node)
169                 if node != nil
170                 {
171                     heads[node!] = nextList
172                 }
173             }
174                 //下一個不是 times + 1的   要自己新建一個node  然後左右兩邊重新連接好
175             else
176             {
177                 let newList = NodeList(node)
178                 
179                 if preList != nil
180                 {
181                     preList?.next = newList
182                 }
183                 newList.pre = preList
184                 newList.next = nextList
185                 nextList?.pre = newList
186                 //這個是也要更換頭
187                 if headList == nextList
188                 {
189                     headList = newList
190                 }
191                 if node != nil
192                 {
193                     heads[node!] = newList
194                 }
195             }
196         }
197     }
198     
199     /**
200      * 這個方法的調用時機是  把一個node從一個nodelist中刪掉 ,然後判斷是不是要不這個nodelist給刪掉
201      * 就是在delete之後, 要不要把整個小鏈表刪掉
202      *
203      * @param nodeList
204      */
205     func modifyHeadList(_ nodeList:NodeList) -> Bool
206     {
207         //為空了才要刪掉整個大鏈表中的這個結點
208         if nodeList.isEmpty()
209         {
210             //要刪的這個  是整個大鏈表的頭部
211             if headList == nodeList
212             {
213                 //新的頭部是老頭部的下一個
214                 headList = headList?.next
215                 if headList != nil
216                 {
217                     headList?.pre = nil
218                 }
219             }
220             else
221             {
222                 //要刪的不是頭
223                 nodeList.pre?.next = nodeList.next
224                 if nodeList.next != nil
225                 {
226                     nodeList.next?.pre = nodeList.pre
227                 }
228             }
229             //也就是 這個是要整個都要刪掉的
230             return true
231         }
232         //不空的話(也就是不只一個)就不要刪    留著
233         return false
234     }
235 }
236 //小鏈表(掛在下面的)
237 class Node:Hashable
238 {
239     static func == (lhs: Node, rhs: Node) -> Bool {
240         return lhs.key == rhs.key && lhs.val == rhs.val && lhs.times == rhs.times
241     }
242     
243     func hash(into hasher: inout Hasher)
244     {
245         hasher.combine(key)
246         hasher.combine(val)
247         hasher.combine(times)
248     }
249     //map中push的key
250     var key:Int
251     //map中對應的value
252     var val:Int
253     //操作的次數
254     var times:Int
255     //小鏈表的上一個
256     var up:Node?
257     //小鏈表的下一個
258     var down:Node?
259     
260     init(_ key:Int,_ val:Int,_ times:Int)
261     {
262         self.key = key
263         self.val = val
264         self.times = times
265     }
266 }
267 
268 //大的鏈表的結點結構  (每一個結點都是一個小鏈表)
269 class NodeList:Hashable
270 {
271     static func == (lhs: NodeList, rhs: NodeList) -> Bool {
272         return lhs.head == rhs.head && lhs.tail == rhs.tail 
273     }
274     
275     func hash(into hasher: inout Hasher)
276     {
277         hasher.combine(head)
278         hasher.combine(tail)
279     }
280     
281     //大鏈表的頭部指針
282     var head:Node?
283     //大鏈表的尾部指針
284     var tail:Node?
285     //大鏈表的前一個結點
286     var pre:NodeList?
287     //大鏈表的下一個結點
288     var next:NodeList?
289     
290     init(_ node:Node?)
291     {
292         self.head = node
293         self.tail = node
294     }
295     
296     //返回這個小鏈表(小鏈表本身又是大鏈表的結點)是不是空的
297     func isEmpty() -> Bool
298     {
299         return head == nil
300     }
301     
302     //小鏈表的頭部添加結點
303     func addNodeFromHead(_ newHead:Node?)
304     {
305         newHead?.down = head
306         head?.up = newHead
307         head = newHead
308     }
309     
310     //刪除小鏈表中的任意一個結點
311     func deleteNode(_ node:Node?)
312     {
313         //只有一個結點
314         if head == tail
315         {
316             head = nil
317             tail = nil
318         }
319         else
320         {
321             //刪除的是小鏈表的頭部
322             if head  == node
323             {
324                 //頭結點變成下一個
325                 head = head?.down
326                 //頭結點的上一個 置空
327                 head?.up = nil
328             }
329                 //刪除的是小鏈表的尾部
330             else if tail  == node
331             {
332                 tail = tail?.up
333                 tail?.down = nil
334             }
335                 //刪除的是鏈表的中間
336             else
337             {
338                 node?.up?.down = node?.down
339                 node?.down?.up = node?.up
340             }
341         }
342         //完全斷鏈
343         node?.up = nil
344         node?.down = nil
345     }
346 }
347 /**
348  * Your LFUCache object will be instantiated and called as such:
349  * let obj = LFUCache(capacity)
350  * let ret_1: Int = obj.get(key)
351  * obj.put(key, value)
352  */

[Swift]LeetCode460. LFU緩存 | LFU Cache