拒了一个只要1.8万的45岁大佬,项目经验吊打我们整个技术部,但。。。
45岁,张口只要1.8万,结果还被拒了。评论区一下就炸了,有人替公司喊冤,说年纪摆在那,管理岗干久了,真进来未必接得住活;也有人替这位大佬憋屈,觉得都把姿态放这么低了,还挑,企业嘴上喊缺人,身体却很诚实。
我看这事最扎心的地方,不是1.8万高不高,是“45岁+大厂/大佬”这几个字一叠上去,HR脑子里警报直接响。不是怕你不会干,是怕你干得太明白,团队压不住,流程糊弄不了,工资倒成了最不重要的那一项。
算法题:账户合并
这题一上来就别急着写并查集代码。 “账户合并”这种题,第一眼我更关心的是:你到底拿什么当人。名字?不靠谱。邮箱?这才像线上系统里真正能拿来做关联的键。
题目给你一堆账户:
[
["John", "[email protected]", "[email protected]"],
["John", "[email protected]", "[email protected]"],
["Mary", "[email protected]"]
]
看着像是按名字分组,其实不是。两个 John 不一定是一个人,但只要邮箱有交集,那这两个账户就该并到一起。这个味道已经很像真实业务里的“主数据归并”了:展示字段可以重复,真正决定归属关系的,是底下那串唯一标识。
这题最顺手的解法就是并查集。 不要硬在账户之间两两比较,那样写着就别扭,复杂度也难看。更自然的做法是:把每个邮箱当成节点,同一账户下的邮箱全部连起来。最后,同一连通分量里的邮箱,就是同一个人。
代码我一般会写得短一点,够用就行:
from collections import defaultdict
classUnionFind:
def__init__(self):
self.parent = {}
deffind(self, x):
if x notin self.parent:
self.parent[x] = x
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
defunion(self, a, b):
pa, pb = self.find(a), self.find(b)
if pa != pb:
self.parent[pb] = pa
主逻辑也不用绕:
defaccountsMerge(accounts):
uf = UnionFind()
email_to_name = {}
for acc in accounts:
name = acc[0]
first_email = acc[1]
for email in acc[1:]:
email_to_name[email] = name
uf.union(first_email, email)
groups = defaultdict(list)
for email in email_to_name:
root = uf.find(email)
groups[root].append(email)
ans = []
for root, emails in groups.items():
ans.append([email_to_name[root]] + sorted(emails))
return ans
这里有两个细节,面试里很容易丢分。
第一,name 不参与 union。 它只是结果展示用。你真拿 name 做合并条件,基本等于把“张三”和“张三”都焊死了,题目当场就做歪。
第二,同一账户里拿第一个邮箱做锚点。 比如 ["John", "a", "b", "c"],只需要把 a-b、a-c 连起来,不用 a-b、b-c、a-c 全连一遍。能少 union 就少 union,这种地方写得干净,代码味道会好很多。
这题时间复杂度主要在最后排序。 并查集那部分接近 O(N),真正花时间的是把每组邮箱排个序。所以整体可以看成:建图归并不重,排序才是大头。
再往下想一步,这题其实不是算法题那么简单。它背后就是一句话:展示数据不可信,关联键才可信。
你把这个判断带到平时写业务里,很多脏逻辑能少走不少弯路。 这题就到这,没什么玄学,认准邮箱做并查集合并,基本就稳了。