算法编程题-LRU & LFU

原题描述

LRU和LFU这两种经典的题目,相信很多同学在面试的时候都有遇到过。今年秋招,我有个师兄,本身学历经历表达能力都是拉满的,他简历上有个项目用到的技术里写了LRU,结果好几个大厂的面试都考了这题,这真的是说明了,如果面试官想要你,在算法编程题是不会为难你的,如果不要你,直接给你一道冷门的Hard题。
LRU和LFU都可以认为是一种内存淘汰算法,在操作系统的分页内存管理、Redis缓存、Mysql缓存等场景都有广泛的应用。他们都是一种策略,在面对内存有限的情况下,需要把一些可能将来不会被访问的内存淘汰出去,而这两种算法的区别就是如何定义将来不会被访问的内存。对于LRU,认为最长时间没有被访问的元素是应该被淘汰的,而LFU则认为访问次数频率最小的元素应该是要被淘汰的。事实上,在真正的应用场景,往往会结合这两种算法来设计内存淘汰算法。
两题leetcode链接:

LRU

思路

这类题目的主要难点在于如何使得Get和Set的操作都是 O ( 1 ) O(1) O(1)的时间复杂度。其实,看到这种要求的复杂度,往往需要用到哈希表。通过哈希表来保存key和其对应的链表节点,然后再维护一条双向链表,便于删除和插入操作。那么思路其实就很明确了。

代码

// ListNode 表示一个双向链表中的一个节点
type ListNode struct {
   
   
	Key  int
	Val  int
    Freq int
	Next *ListNode
	Prev *ListNode
}

func NewListNode(key, val int) *ListNode {
   
   
	return &ListNode{
   
   Key: key, Val: val}
}

// List 定义一个双向链表
type List struct {
   
   
	Head *ListNode
	Tail *ListNode
}

// NewList 创建一个空的双向链表,并且使用伪头部和伪尾部
func NewList() *List {
   
   
	head := NewListNode(-1, -1)
	tail := NewListNode(-1, -1)
	head.Next = tail
	tail.Prev = head
	return &List{
   
   Head: head, Tail: tail}
}

// AppendNodeHead 将某个节点插在链表头部
func (l *List) AppendNodeHead(node *ListNode) {
   
   
	node.Next = l.Head.Next
	l.Head.Next.Prev = node
	node.Prev = l.Head
	l.Head.Next = node
}

// DeleteNode 删除一个指定的节点
func (l *List) DeleteNode(node *ListNode) {
   
   
	// 避免panic
	if node.Prev 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值