Python技术迷

拒了一个只要1.8万的45岁大佬,项目经验吊打我们整个技术部,但。。。

45岁,张口只要1.8万,结果还被拒了。评论区一下就炸了,有人替公司喊冤,说年纪摆在那,管理岗干久了,真进来未必接得住活;也有人替这位大佬憋屈,觉得都把姿态放这么低了,还挑,企业嘴上喊缺人,身体却很诚实。

Image

我看这事最扎心的地方,不是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),真正花时间的是把每组邮箱排个序。所以整体可以看成:建图归并不重,排序才是大头。

再往下想一步,这题其实不是算法题那么简单。它背后就是一句话:展示数据不可信,关联键才可信。

你把这个判断带到平时写业务里,很多脏逻辑能少走不少弯路。 这题就到这,没什么玄学,认准邮箱做并查集合并,基本就稳了。