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