hr吐槽:听到这个离职理由,我直接 Pass~
我在网上看到个HR吐槽帖,开头就很扎心:前面聊了半小时,技术也还行,结果一问离职原因,候选人一句话把自己聊没了。
他说得很实在:跟同事闹矛盾,领导还偏心,自己天天上班像踩地雷,干脆辞职。
你说这话假吗?大概率不假。问题也就出在这儿——面试不是找树洞,你一股脑倒苦水,HR耳朵里自动翻译成四个字:不好相处。
站在求职者这边,我也能理解,谁没遇到过糟心同事和拉偏架的领导?但面试这场合,真别太耿直。
职场这玩意儿,很多时候不是你说得真,人家就一定敢要。
算法题:敲击计数器
有个题看着不难,真写的时候挺容易把自己绕进去:设计一个“敲击计数器”,支持两件事。
hit(timestamp) # 某个时间点来了一次敲击
get_hits(timestamp) # 返回最近 300 秒内一共多少次敲击
先别急着上复杂结构,这题核心就一句话:只关心最近 300 秒,窗口外的数据可以直接扔掉。
很多人第一反应是拿一个数组把所有时间都记下来,get_hits() 时再扫一遍统计。能做,但味道不对。因为这题的时间是不断增长的,旧数据一旦过期,继续留着只会拖慢查询。
比较顺手的做法是队列。
每来一次敲击,就把时间戳塞进队尾。查询时,把队头那些已经超过 300 秒范围的记录弹掉,剩下多少个,答案就是多少个。
先看核心代码:
from collections import deque
classHitCounter:
def__init__(self):
self.q = deque()
defhit(self, timestamp: int) -> None:
self.q.append(timestamp)
defget_hits(self, timestamp: int) -> int:
border = timestamp - 300
while self.q and self.q[0] <= border:
self.q.popleft()
return len(self.q)
这里边界别写错了。题目要的是“最近 300 秒”,如果当前是 300,那有效区间其实是 (0, 300],所以 <= timestamp - 300 的都该清掉。
拿一组数据过一下:
counter = HitCounter()
counter.hit(1)
counter.hit(2)
counter.hit(3)
print(counter.get_hits(4)) # 3
counter.hit(300)
print(counter.get_hits(300)) # 4
print(counter.get_hits(301)) # 3
到 301 秒时,时间戳 1 这一条已经不在窗口里了,所以只剩 2、3、300 三次。
这题再往前走一步,会有人问:如果同一秒打了很多次,队列里不是会塞很多重复值?确实会。所以还能再压一版,把“时间戳 + 次数”合并存。
from collections import deque
classHitCounter:
def__init__(self):
self.q = deque()
self.total = 0
defhit(self, timestamp: int) -> None:
if self.q and self.q[-1][0] == timestamp:
self.q[-1][1] += 1
else:
self.q.append([timestamp, 1])
self.total += 1
defget_hits(self, timestamp: int) -> int:
border = timestamp - 300
while self.q and self.q[0][0] <= border:
_, cnt = self.q.popleft()
self.total -= cnt
return self.total
这一版更像线上写法。查询不用每次 len() 去数,直接维护一个 total。而且同一秒的大量请求不会把队列撑得太夸张。
这题本质不在“计数”,而在“滑动窗口怎么清旧数据”。一旦想明白“过期就删”,代码其实很短。面试里真正容易错的,通常不是数据结构,而是那个窗口边界。这个地方手算两组时间戳,基本就稳了。