某节HR:现在面试最怕遇到一上来就卑微到骨子里的候选人,工资不敢问,加班不敢提,说啥他都点头,恨不得把姿态踩到地底下去讨好
前某节HR这吐槽,我一眼就懂。工资不敢问,加班不敢提,岗位边界也不问,问啥都点头,笑得特别乖,乖到你都不敢录。你以为他稳定,其实HR心里大概率在打鼓:这人到底是太缺工作了,还是压根没底气,进来之后会不会一碰就碎。
评论区也有人说得挺狠,说这种人不是懂事,是先把自己谈成了“廉价劳动力”。还有人说,面试本来就是双向挑人,你上来先把自己压扁了,后面吃亏基本跑不了。
职场又不是古代卖身契,真没必要把姿态放那么低。你可以礼貌,可以克制,但别一副“只要收留我,怎样都行”的样子。HR看完估计都得犯嘀咕:你现在这么好说话,八成以后也最容易委屈、最容易崩。
算法题:打开转盘锁
锁一转,四个拨轮,每次只能拧一格,0000 要走到目标值。看着像暴力题,但真上手写,很多人会先把自己绕进去:到底该从目标往回推,还是从起点往前搜?这题我第一眼就不太信 DFS,分支太散,走偏了基本就是白跑。
这题更像线上排障里那种“最少步数”问题。你不关心怎么走得花,只关心第一个走到目标的是不是最短。所以直接上 BFS。每扩一层,就是多拧一次。第一次撞到 target,答案就出来了。
麻烦点不在搜索本身,在两个细节:
第一,deadends 这种死亡节点得先拦掉,不然队列会反复灌脏数据。 第二,拨轮不是普通加减,9 往上是 0,0 往下是 9,这地方代码写丑了,后面很容易看吐。
我一般会把“扩展下一个状态”单独拎出来,别把轮子旋转逻辑糊在 BFS 主循环里。
from collections import deque
defneighbors(state: str):
for i, ch in enumerate(state):
digit = int(ch)
up = state[:i] + str((digit + 1) % 10) + state[i+1:]
down = state[:i] + str((digit - 1) % 10) + state[i+1:]
yield up
yield down
主逻辑就干净很多:
defopenLock(deadends, target: str) -> int:
dead = set(deadends)
start = "0000"
if start in dead:
return-1
if target == start:
return0
q = deque([(start, 0)])
visited = {start}
while q:
state, step = q.popleft()
for nxt in neighbors(state):
if nxt in dead or nxt in visited:
continue
if nxt == target:
return step + 1
visited.add(nxt)
q.append((nxt, step + 1))
return-1
这段代码不花,但够稳。
比如 deadends = ["0201","0101","0102","1212","2002"],target = "0202",答案是 6。BFS 会一层层往外扩,遇到死锁就跳过,遇到访问过的也不回头。这种题最怕的不是不会写,是重复状态没去重,队列越跑越肥,最后把自己耗死。
还有人喜欢双向 BFS,这题也能做,而且会更快。只是面试里如果先把单向 BFS 写稳,其实已经够用了。别一上来就炫优化,基础版都没控住,优化只是把 bug 写复杂。
这题真正的判断点就一句:求最少步数,状态图边权一致,优先 BFS。