[Swift]LeetCode460. LFU緩存 | LFU Cache
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)緩存的數據結構。它應該支持以下操作:get
和 put
。
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