Python技术迷

年薪80万程序员,34岁考上了事业编,在教育局工作,每个月工资6000块。他爸妈现在逢人就夸自己儿子有出息,说比之前在大厂上班强多了

朋友原来在腾讯写代码,一年拿八十来万,放在打工人里已经算很能打了。结果34岁那年,他转头考回老家,进了教育局,一个月到手六千左右。

你以为家里会心疼收入掉这么多?没有。他爸妈现在见人就夸,儿子有本事,进单位了,端上铁饭碗了。以前在大厂赚钱,他们也高兴,但那种高兴像是“孩子会挣钱”。现在不一样了,像是祖坟冒青烟。

Image

这事挺真实的。很多父母眼里,钱多不一定稳,听着也不一定体面。你说你在大厂年薪高,他们担心你哪天被裁;你说你在教育局,他们立马觉得这辈子踏实了。

程序员听完估计沉默,年薪80万干不过一个“有编制”,这职场价值观,真不是一套系统。

算法题:喧闹和富有

richer 这个入参挺容易把人带偏。

它不是让你去给所有人按财富排序,也不是让你补全一张“谁比谁有钱”的大表。题目真正要查的是:对每个人 x,在所有确定比他有钱的人里,再加上他自己,找一个最安静的人。

比如:

richer = [[1, 0], [2, 1], [3, 1]]
quiet = [5, 3, 6, 1]

[1, 0] 表示 1 比 0 有钱。

那查 0 的时候,不只看 1,还得继续往上看 2、3。因为 2、3 也可能通过关系链比 0 有钱。

这题我一般不会先想着排序。关系是单向的,而且每个人都要查一遍,如果每次都从头扫 richer,代码看着没毛病,跑大一点就开始发虚。

先把边反过来建。

原始关系是:

rich -> poor

但查询某个人时,我们要找“谁比他有钱”,所以更适合建成:

poor -> [rich1, rich2, ...]

代码可以这么写:

from typing import List

classSolution:
defloudAndRich(self, richer: List[List[int]], quiet: List[int]) -> List[int]:
        n = len(quiet)
        richer_map = [[] for _ in range(n)]

for rich, poor in richer:
            richer_map[poor].append(rich)

        ans = [-1] * n

defpick(person: int) -> int:
if ans[person] != -1:
return ans[person]

            best = person

for rich_person in richer_map[person]:
                candidate = pick(rich_person)

if quiet[candidate] < quiet[best]:
                    best = candidate

            ans[person] = best
return best

for i in range(n):
            pick(i)

return ans

这里有个细节,pick(person) 返回的不是安静值,而是人的编号。

这个地方我见过有人直接返回 quiet 值,前面跑着挺顺,最后发现题目要的是下标,又绕回去补映射。没必要,直接保存人就行。

再看一下递归里这几行:

best = person

for rich_person in richer_map[person]:
    candidate = pick(rich_person)

if quiet[candidate] < quiet[best]:
        best = candidate

意思很直白。

先假设自己最安静,然后去所有比自己有钱的人那里问一遍:你那条链上最安静的是谁?

如果对方链上找到的人更安静,就更新。

这题真正省时间的地方在 ans 这个缓存。

比如很多人都能追到同一个富人链,如果不缓存,每个人都会把那条链重新走一遍。缓存后,一个人的答案算过一次,后面直接拿。

这类题别急着写复杂结构。

先把问题方向掰正:我要从当前人往“更有钱的人”走。

再把重复计算掐掉:一个人的答案只算一次。

剩下就是 DFS。题目名字叫“喧闹和富有”,代码里其实没什么喧闹的地方,主要是别被 richer 那个方向骗了。