LRU cache实现,还是使用伪头部和伪尾部节点写代码更加简单
class Node: def __init__(self, key, val): self.key = key self.val = val self.prev = None self.next = None class LRUCache: def __init__(self, capacity): self.capacity = capacity self.hashmap = {} # 使用伪头部和伪尾部节点 self.head = Node(0, 0) self.tail = Node(0, 0) self.head.next = self.tail self.tail.prev = self.head def get(self, key): if key in self.hashmap: node = self.hashmap[key] self._remove(node) self._add(node) return node.val return -1 def put(self, key, value): if key in self.hashmap: self._remove(self.hashmap[key]) node = Node(key, value) self._add(node) self.hashmap[key] = node if len(self.hashmap) > self.capacity: node = self.head.next self._remove(node) del self.hashmap[node.key] def _remove(self, node): prev = node.prev next = node.next prev.next = next next.prev = prev def _add(self, node): prev = self.tail.prev prev.next = node self.tail.prev = node node.prev = prev node.next = self.tail # 创建一个容量为2的LRU缓存 cache = LRUCache(2) # 插入键值对("192.168.1.1", 1) cache.put("192.168.1.1", 1) assert cache.get("192.168.1.1") == 1 # 返回1 # 插入键值对("192.168.1.2", 2) cache.put("192.168.1.2", 2) assert cache.get("192.168.1.2") == 2 # 返回2 # 再次访问键"192.168.1.1" assert cache.get("192.168.1.1") == 1 # 返回1 # 插入键值对("192.168.1.3", 3),这会导致键"192.168.1.2"被删除,因为它是最近最少使用的键 cache.put("192.168.1.3", 3) assert cache.get("192.168.1.2") == -1 # 返回-1,因为键"192.168.1.2"已经被删除 # 再次访问键"192.168.1.1" assert cache.get("192.168.1.1") == 1 # 返回1 # 插入键值对("192.168.1.4", 4),这会导致键"192.168.1.3"被删除,因为它是最近最少使用的键 cache.put("192.168.1.4", 4) assert cache.get("192.168.1.3") == -1 # 返回-1,因为键"192.168.1.3"已经被删除
核心:
(1)只要访问过,或者新节点,就将节点放到tail,区别无非是访问的节点修改前后link;
(2)只要是删除,就将节点从head里remove。
void lru_add_to_tail(struct brief_asset_info *node) { // 将对应的节点添加到双向链表的尾部,作为LRU的最新节点 node->prev = g_asset_portray_lru.dummy_tail->prev; node->next = g_asset_portray_lru.dummy_tail; g_asset_portray_lru.dummy_tail->prev->next = node; g_asset_portray_lru.dummy_tail->prev = node; } void lru_move_to_tail(struct brief_asset_info *node) { // 移动节点到到双向链表的尾部,作为LRU的最新节点 node->prev->next = node->next; node->next->prev = node->prev; lru_add_to_tail(node); } struct brief_asset_info *lru_delete_head() { struct brief_asset_info *node = g_asset_portray_lru.dummy_head->next; g_asset_portray_lru.dummy_head->next = node->next; node->next->prev = g_asset_portray_lru.dummy_head; return node; }
实际C代码应用伪代码:
struct asset_nlog_ua *asset_check_iot_xxx(struct asset_info *info, bool *is_counterfeit) { struct ip_mac ip_mac_key = {0}; ip_mac_key.ip = info->ip; ip_mac_key.mac = info->mac; struct ase_hash *asset_portray = g_asset_portray_lru.asset_portray_map; struct brief_asset_info *found_info = asset_find_iot_portray(&ip_mac_key, asset_portray); struct asset_nlog_ua *event = NULL; if (found_info != NULL) { if (check_asset_conflict(found_info, info)) { // 误报检查,非误报才生成事件 if (!asset_check_counterfeit_false_alarm(&found_info->ip_mac_key.ip, &found_info->ip_mac_key.mac)) { *is_counterfeit = true; event = asset_generate_iot_event(found_info, info); // 刷新LRU cache,更新访问的最新资产 lru_move_to_tail(found_info); return; } } // 资产属性不冲突,或者冲突但属于误报,更新已经存在的画像 asset_update_iot_portray(info, found_info, asset_portray); // 刷新LRU cache,更新修改后的最新资产 lru_move_to_tail(found_info); } else { // 新建画像,需要考虑是否满规格,满规格情况下需要删除LRU中的老资产 if (g_asset_portray_lru.asset_portray_map->n_entry >= g_asset_portray_lru.capacity) { ASE_INFO(ASE_CATE_ASSET, "[iot] asset_portray_map exceed max capacity, will asset_delete_oldest_portray."); asset_delete_oldest_portray(asset_portray); } struct brief_asset_info *new_info = asset_insert_iot_portray(info, asset_portray); // 刷新LRU cache,更新新建的最新资产 lru_add_to_tail(new_info); } return event; }