トピック:
トピックリンク: https://leetcode-cn.com/problems/lfu-cache/
問題解決のアイデア:
LFUは、頻繁に使用される最小と呼ばれ、思考の最も最近使用されるアルゴリズムは次のとおりです。データの数が少しを使用している場合は、メモリがほぼいっぱいになったとき、それは、そのように限定されたメモリ空間で、将来的に使用されているの比較的低い確率である必要があり、またはケース内のスペースを節約するために、他のデータを与えるために、このデータの一部を削除するには、完全な、所与の優先順位です
LFUおよびLRUは最低使用LRUと呼ばれ、対処したい、思考の最も最近使用されるアルゴリズムは次のとおりです。長いデータが使用されていない場合は、使用の可能性が比較的低い、後にいくつかの時間、優先順位ので、このデータの一部を削除
LFUのアイデアが基づいている使用回数 LRUのアイデア、のがに基づいており、時間の
O(1)時間の複雑性を達成するために、このトピックに戻り、最も一般的な方法は次のとおりです。
検索:使用のハッシュテーブル
インサート:リスト
なるように単独リンクリストは、既存の要素を削除するとき、あなたはフロントとリアを接続するノードのノードを削除する必要があるため、リストを横断する必要があるので、リストには、二重リンクリストに改定されます
検索:ハッシュテーブル
挿入+削除:二重リンクリスト
この問題の全体的なアイデアは比較的明確です:
変数:
- キーマップ:値が置かれたとき、ハッシュテーブル、キー、キー値、値がfreqMap中の二重リンクリスト保存されているノードアドレス(ポインタ)
- freqMap:ハッシュテーブル、周波数のキー値は、値が二重リンクリストの時系列に従って、リンクされたリストは、各ノードに格納されている現在の周波数です。
- キーの値を入れます
- 値値置きます
- ノードでこの値を保存する必要があるので、使用頻度の現在の値は、キーマップから発見されたとして、あなたは、中キー値freqMapを知ることができません
- ノードノード前に現在の
- 現在のノードの次の次のノード
- 容量:保存することができる値の最大数
- サイズ:現在使用可能な値の数
- minFreq:周波数の最小周波数は、現在使用されて
プロセス:
- 取得(自己、キー:INT)操作:
- 存在する場合、対応するノードのノードが得られ、存在しない、リターン-1場合は、キー値を検索キーマップ
- ノードは、対応する周波数FREQ = FREQ + 1を更新し、リンクされたリスト内の現在のノードを削除
- 更新されたノードFREQのfreqMapは、対応する新しいFREQに再挿入リストの先頭をリスト全体が順序付けられていることを確実にするためには、
- ノード値に格納された値を返します。
- プット(自己、キー:int型、値:int型)操作:
- キーマップのキー値を見つけ、そうでない場合、キーマップ及びfreqMapに新しいキー値を挿入し、値由来の本、ノード対応FREQが更新された場合、ノード位置の更新、取得操作と同じように更新2ステップ3,4
- 値のサイズ値の更新保存数
- サイズ<=容量、次いで出口;サイズ場合>もし容量、minFreqによれば、リストに対応する最後のノードを削除
コードの実装:
class Node:
def __init__(self, key: int, value: int, freq = 0, prev_node = None, next_node = None):
self.key = key
self.value = value
self.freq = freq
self.prev_node = prev_node
self.next_node = next_node
def insert(self, node) -> None:
# insert操作为常用操作,添加一个接口便于使用
node.prev_node = self
node.next_node = self.next_node
self.next_node.prev_node = node
self.next_node = node
def create_linked_lst() -> set((Node, Node)):
# 创建首尾dummy节点,避免一些临界情况的判断
# 并且方便管理
head = Node(0,0)
tail = Node(0,0)
head.next_node = tail
tail.prev_node = head
return (head, tail)
class LFUCache:
def __init__(self, capacity: int):
self.__freq_map = collections.defaultdict(create_linked_lst)
self.__key_map = {}
self.__capacity = capacity
self.__size = 0
self.__min_freq = 0
def __delete_node(self, node: Node) -> int:
# 在put中调用__update_node时,node尚未设置前后置节点
# 此时node.prev_node是None,需要保护性判断
if node.prev_node:
node.prev_node.next_node = node.next_node
node.next_node.prev_node = node.prev_node
# 如果删除完成后,当前频率下没有有效节点了,则在__freq_map频率表中删除此列表
if node.prev_node is self.__freq_map[node.freq][0] and node.next_node is self.__freq_map[node.freq][-1]:
self.__freq_map.pop(node.freq)
return node.key
def __update_node(self, node: Node):
node.freq += 1
self.__delete_node(node)
self.__freq_map[node.freq][-1].prev_node.insert(node)
if node.freq == 1:
# 如果当前频率是1,不可能有比1还小的频率,此时更新最小频率为1
self.__min_freq = 1
elif self.__min_freq == node.freq - 1:
# 如果最小频率是当前node更新前的频率,需要判断之前的频率链表中,是否还有有效节点
# 如果没有,则删除对应的链表
head, tail = self.__freq_map[node.freq - 1]
if head.next_node is tail:
self.__min_freq = node.freq
def get(self, key: int) -> int:
if key in self.__key_map:
# 更新节点位置
self.__update_node(self.__key_map[key])
return self.__key_map[key].value
return -1
def put(self, key: int, value: int) -> None:
if self.__capacity > 0:
if key in self.__key_map:
# 如果当前key已经在__key_map中,则更新对应的值
node = self.__key_map[key]
node.value = value
else:
# 如果不存在,则创建一个新节点
node = Node(key, value)
self.__key_map[key] = node
self.__size += 1
if self.__size > self.__capacity:
# 节点个数已满,删除使用频率最小的存在时间最长的节点
self.__size -= 1
del_key = self.__delete_node(self.__freq_map[self.__min_freq][0].next_node)
self.__key_map.pop(del_key)
self.__update_node(node)