10-10 LeetCode 460. LFU Cache
Description
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?
為最不頻繁使用緩存技術設計一種數(shù)據(jù)結構,它需要支持get和put方法。
get(key) - 返回key對應的值如果key存在于緩存中,否則返回-1.
put(key, value) - 插入key, value鍵值對,當緩存區(qū)滿了以后,需要刪除最不頻繁使用的key, value對。為了滿足這個目的,當多個key有相同的頻率時,上一次使用時間距離當前時間最長的key將被刪除。
要求:
所有操作在O(1)時間復雜度內完成
知識補充
Least Frequently Used cache,如果我沒記錯的話,這是在學計算機組成原理時學到的一個知識。cpu對緩存區(qū)域的數(shù)據(jù)讀取速度比對外存中的數(shù)據(jù)讀取要快很多,所以為了提高cpu的運行速度,將一些常用的數(shù)據(jù)預先存入緩存,就可以減少數(shù)據(jù)讀取的時間消耗,從而提高cpu運行效率。但是我們不可能提前知道cpu需要什么數(shù)據(jù),緩存的大小也有限,不可能存下所有數(shù)據(jù),所以設計一個算法來控制存入緩存中的數(shù)據(jù)是有必要的。想象一下,cpu每次取數(shù)據(jù),緩存中都沒有,都需要從外存中獲取,那么這個緩存設計的就沒有一點意義。所以需要提高cpu命中緩存中數(shù)據(jù)的幾率。因此有很多相關的算法,可以上網(wǎng)搜索一下。
Solution 1:
這道題的難點就在于要求所有操作的時間復雜度都為O(1),于是我們很自然而然的就想到用哈希表這種數(shù)據(jù)結構,在python中字典就是這樣的一種結構。不管數(shù)據(jù)量有多大,在字典中查詢key值的時間復雜度都為O(1)。
但是光一個字典肯定是不夠的,因為我們需要刪除最不頻繁使用的key。因此還需要一個字典來存儲每一個頻率下對應的key有哪些,例如頻率1次的有key1,key2,頻率3次的有key3...,
想到這我就開始寫代碼了,寫完提交后才發(fā)現(xiàn),這樣的時間復雜度還是不能達到O(1)。因為通過key不能在O(1)時間內找到key對應的頻率,所以還需要額外的一個字典來存儲key以及key對應的頻率。現(xiàn)在應該是很清晰了,我直接貼上代碼,代碼的大多數(shù)位置也有注釋。
代碼:
class LFUCache(object):
def __init__(self, capacity):
"""
:type capacity: int
"""
self.capacity = capacity
self.cache = {} # 存放存入的鍵值對
self.frequency = {} # 存放每個頻率中出現(xiàn)的key,如{1:[key1,key2], 2:[key3]}
self.cache_index = {} # 存放key對應的頻率, 如{key1:1, key2:1, key3:2}
def get(self, key):
"""
:type key: int
:rtype: int
"""
if key not in self.cache:
return -1
index = self.cache_index[key]
self.frequency[index].remove(key)
if self.frequency[index] == []:
del self.frequency[index]
if index+1 in self.frequency:
self.frequency[index+1].append(key)
else:
self.frequency[index+1] = [key]
self.cache_index[key] += 1
return self.cache[key]
def put(self, key, value):
"""
:type key: int
:type value: int
:rtype: void
"""
if self.capacity <= 0:
return
if key in self.cache:
# 如果put一個已經(jīng)存在的key,修改它的value和frequency
self.cache[key] = value
self.get(key)
return
if len(self.cache) == self.capacity:
for times in self.frequency: # 因為字典有序,所以第一個肯定是頻率最小的,刪除后通過break退出循環(huán)
key_of_cache = self.frequency[times][0] # 取出頻率最小的key,并刪除
del self.cache[key_of_cache]
del self.cache_index[key_of_cache]
self.frequency[times] = self.frequency[times][1:]
if self.frequency[times] == []:
del self.frequency[times]
break
# 插入一個新值,頻率初始值為1
self.cache[key] = value
if 1 in self.frequency:
self.frequency[1].append(key)
else:
self.frequency[1] = [key]
self.cache_index[key] = 1
感想
每天做這個還是耗費挺多時間的,因為水平不高,做一題就需要很長時間,然后還需要記錄下來,我可能要減少發(fā)布的頻率了...............