hr对我有意思,咋办??
刚看到个贴子,说网友面试小厂,技术面过后,HR突然开始问“你单身吗”“要不要一起吃饭”,他拒绝后担心被挂简历。
我作为程序员第一反应是——这不是技术问题,这是职场边界问题。面试就像写代码,需求明确、接口清晰才能高效运行,突然插入这种“感情需求”,就是强行往代码里塞彩蛋,还可能引发异常。网友里有人说“无所谓,多一个offer少一个没差”,也有人劝“要委婉处理”。我觉得关键在于保持专业态度,拒绝时不用翻脸,但要让对方知道:我来是谈工作,不是谈恋爱。
说到底,职场跟代码一样,逻辑清晰、边界明确,才能稳定运行。守住底线,机会自然还会有的。【备注:文末可领最新资料】
面试题:LFU 缓存
说真话啊,昨天晚上十一点多我在小区地库转圈,车位老是抢不到,脑子里就一直琢磨缓存淘汰这事儿。你们知道吧,平时写接口,Redis 热点还好说,业务侧自己搞个本地缓存,过一会儿就有人问:为啥我放进去的东西被干掉了?我说你用的是 LFU,不常用的就会被“礼貌请出”。他愣住了,哦那个…最少使用频率的缓存对吧,频率低就滚蛋的那个。对,就是它。
先把直觉捋顺:为什么是 LFU
场景很生活化:你家鞋柜就两层,能放 N 双。天天穿的运动鞋肯定不能丢,对吧;那双婚礼穿过一次的皮鞋,基本吃灰。LFU 的意思就是——统计每双鞋被穿的次数,次数最少的先扔出去。它和 LRU(最近最少使用)不太一样,LRU看“最近”,LFU看“次数”。当请求分布很稳定时,LFU更靠谱,因为它能把“常年热门”留住。嗯…别抬杠,抬也行,等我把车停好再说,哈哈。
O(1) 的两个哈希表
脑袋里过一遍就行,别被名词吓到。关键就两个映射:
key -> 节点(node):节点里放值、当前频次freq,以及双向链表指针。freq -> 双向链表:同一频次的键排成一条链,链里再按“最近使用”排一下,这样并列时能淘汰最旧的那一个。
还有个小变量 minfreq,记住当前库里最小的频次,清理时不用全图搜索,直接去 freq[minfreq] 的链尾巴抠一个出来,手起刀落。
操作就两件:
get(key):命中就把这个键从原频次链表摘掉,freq+1后丢到新链表头。如果原链表空了,而且它正好是minfreq,那就把minfreq加一。put(key, val):容量满了就去minfreq那条链的尾部淘汰一个。新键进来频次从 1 开始,minfreq置 1。若是更新已有键,就顺手提高频次。
说到这儿我去把窗户关一下…好了继续。
classNode:
__slots__ = ("key", "val", "freq", "prev", "next")
def__init__(self, key, val, freq=1):
self.key = key
self.val = val
self.freq = freq
self.prev = None
self.next = None
classDLinkedList:
def__init__(self):
self.head = Node(None, None) # dummy
self.tail = Node(None, None) # dummy
self.head.next = self.tail
self.tail.prev = self.head
self.size = 0
defappendleft(self, node):
node.next = self.head.next
node.prev = self.head
self.head.next.prev = node
self.head.next = node
self.size += 1
defpop(self, node=None):
if self.size == 0:
returnNone
ifnot node:
node = self.tail.prev
node.prev.next = node.next
node.next.prev = node.prev
node.prev = node.next = None
self.size -= 1
return node
def__len__(self):
return self.size
classLFUCache:
def__init__(self, capacity: int):
self.cap = capacity
self.size = 0
self.minfreq = 0
self.key2node = {}
self.freq2list = {}
def_touch(self, node: Node):
freq = node.freq
lst = self.freq2list[freq]
lst.pop(node)
if len(lst) == 0:
if self.minfreq == freq:
self.minfreq += 1
# 可选:节省内存
# del self.freq2list[freq]
node.freq += 1
self.freq2list.setdefault(node.freq, DLinkedList()).appendleft(node)
defget(self, key: int) -> int:
if key notin self.key2node or self.cap == 0:
return-1
node = self.key2node[key]
self._touch(node)
return node.val
defput(self, key: int, value: int) -> None:
if self.cap == 0:
return
if key in self.key2node:
node = self.key2node[key]
node.val = value
self._touch(node)
return
if self.size == self.cap:
lst = self.freq2list[self.minfreq]
evicted = lst.pop() # 最旧的
del self.key2node[evicted.key]
self.size -= 1
node = Node(key, value, 1)
self.key2node[key] = node
self.freq2list.setdefault(1, DLinkedList()).appendleft(node)
self.minfreq = 1
self.size += 1
# 小试一下
if __name__ == "__main__":
cache = LFUCache(2)
cache.put(1, 10) # {1:10,freq1}
cache.put(2, 20) # {1,2}
cache.get(1) # 1 的 freq=2
cache.put(3, 30) # 淘汰 key=2(freq=1 最旧)
print(cache.get(2)) # -1
print(cache.get(3)) # 30
cache.get(3) # freq=2
cache.put(4, 40) # 淘汰 key=1(freq=2? 不,1 的freq=2但更旧;注意并列走LRU)
print(cache.get(1), cache.get(3), cache.get(4)) # -1 30 40
有人问并列时咋裁决?我前面说了,链表里按最近使用排序,越靠尾越老,平票就按“谁最久没被碰过”踢谁。还有个坑,容量是 0 的时候,啥也别干,不然面试官会坏笑。至于复杂度,get/put 都是摁着 O(1) 去做的,手法就是哈希表 + 多条双向链表。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领