Python技术迷

同事被辞退了,她要求N+1的赔偿,公司同意了人事经理说:你基本工资2800,一共干了4年,补偿金是14000,同事却不认可

刚看到个贴子,说同事被辞退后要求 N+1,公司答应了,但人事按她 2800 的基础工资来算,只给了 14000,她当场不认可。

Image

我觉得这事吧,核心问题就是“大家眼里的工资”和“公司认定的工资”往往不是一个概念。网友们有人说公司耍赖,也有人说同事不懂法,我看了看,其实各有道理,但不能混为一谈。

补偿金怎么算,法律上确实是按“应发而非底薪”来算,可不少企业就喜欢在合同里藏猫腻,到头来吃亏的还是员工。

换个角度想,同事不认可很正常,干了四年,谁都不想被当成“2800 的人”。但情绪归情绪,维权还是得按规则来,不能光靠拍桌子。

不过话说回来,公司能主动给 N+1 其实算给面子,接下来就看她能不能把数字谈清楚。【备注:文末可领最新资料】

面试题:寻找用户推荐人

“寻找用户推荐人”这个算法题,说白了就是——给你一堆“谁推荐了谁”的关系,让你在代码里快速算出:某个用户是被谁推荐的、他的上级上上级是谁、一路往上最终的“老祖宗推荐人”是谁。

一般推荐关系表,很像这样一坨数据:

relations = [
# user_id, referrer_id
    (2, 1),
    (3, 2),
    (4, 2),
    (5, 3),
]

意思就是: 2 的推荐人是 1,3 的推荐人是 2,4 的推荐人是 2,5 的推荐人是 3。

为了好查,我们通常先转成一个字典,方便 O(1) 查推荐人:

ref_map = {
2: 1,
3: 2,
4: 2,
5: 3,
}

这样给你一个 user_id,比如 5,你一查 ref_map[5] 就知道他的直接推荐人是 3。

需求一:找“直接推荐人”

最简单的版本,其实就一行逻辑:

给我 user_id,我告诉你它的直接推荐人是谁; 如果没有(比如自己就是活动入口用户),就返回 None。

代码很直接:

defget_direct_referrer(user_id: int, ref_map: dict[int, int]) -> int | None:
return ref_map.get(user_id)

这个函数时间复杂度是 O(1),因为字典就是哈希表查找。

需求二:找“整条推荐链”

真实业务里更常见的是这种问题:

运营说:给我看一下这个用户的完整推荐链,从他一路往上,看看是谁带来的。

比如 5 -> 3 -> 2 -> 1 这样一条链。 那我们可以一路往上“爬”,直到找不到推荐人为止:

defget_ref_chain(user_id: int, ref_map: dict[int, int]) -> list[int]:
    chain = []
    cur = user_id
    visited = set()  # 防止有人数据写炸了,出现环形推荐

while cur in ref_map:
        ref = ref_map[cur]

# 检测环,例如 A->B, B->C, C->A 这种错误数据
if ref in visited:
raise ValueError(f"检测到推荐关系存在环,出问题的用户: {ref}")
        visited.add(ref)

        chain.append(ref)
        cur = ref

return chain

用上面的例子:

ref_map = {2: 1, 3: 2, 4: 2, 5: 3}
print(get_ref_chain(5, ref_map))  # [3, 2, 1]

这个算法的时间复杂度是 O(k),k 是链条长度。 在正常业务里,推荐链不会特别长(几十级就很夸张了),所以非常够用。

需求三:找“最终推荐人”(顶级老大)

有时候产品只关心:“这个用户最终算谁名下的?” 比如用来算“拉新人数”“团队业绩”等等,只认最顶上那一个。

在刚才的链条函数基础上,其实就是取最后一个:

defget_root_referrer(user_id: int, ref_map: dict[int, int]) -> int | None:
    cur = user_id
    visited = set()

    root = None
while cur in ref_map:
        ref = ref_map[cur]

if ref in visited:
raise ValueError(f"检测到推荐关系存在环,出问题的用户: {ref}")
        visited.add(ref)

        root = ref
        cur = ref

return root

例子:

print(get_root_referrer(5, ref_map))  # 1
print(get_root_referrer(2, ref_map))  # 1
print(get_root_referrer(1, ref_map))  # None(自己就是顶了)

再往前一步:批量用户怎么高效查?

上面这俩函数,对“单个用户”都很好用。 但如果你要一次性查 10 万个用户的“最终推荐人”,简单粗暴的做法就是循环调用:

defget_all_root_referrers(users: list[int], ref_map: dict[int, int]) -> dict[int, int | None]:
    cache: dict[int, int | None] = {}  # 记忆化,避免重复算
    result: dict[int, int | None] = {}

defhelper(u: int) -> int | None:
if u in cache:
return cache[u]
        root = get_root_referrer(u, ref_map)
        cache[u] = root
return root

for u in users:
        result[u] = helper(u)

return result

为什么要搞一个 cache? 因为很多用户会共享同一条上游链条,比如 5 和 3 的最终推荐人都是 1,算过一遍之后直接复用结果,可以省掉很多重复遍历,整体复杂度从“看起来 O(n * 链长)”变成“接近 O(总人数 + 总边数)”。

还可以顺带检查几种“脏数据”

既然都写算法了,顺手就能帮你检查一些常见脏数据问题:

  1. 环形推荐前面已经在 while 里加了 visited 检查,一旦发现有人绕圈圈,直接抛异常或打告警。这种情况通常是导数导炸了,得运维同学回滚数据。

  2. 推荐人不存在比如 ref_map[5] = 99999,但 99999 这个用户根本就不在用户表里。 这个可以在构建 ref_map 前,先把全量用户 id 做成一个 set,检查一下:

    defbuild_ref_map(relations, valid_users: set[int]) -> dict[int, int]:
        ref_map = {}
    for user, ref in relations:
    if ref notin valid_users:
    # 可以选择直接跳过、记录日志、或者抛异常
    raise ValueError(f"推荐人 {ref} 不存在,对应用户 {user}")
            ref_map[user] = ref
    return ref_map

这道“寻找用户推荐人”的算法题,其实就是在一个“每个点只有一个父亲”的有向图里,往上走链路的问题:

  • 用 dict 存 user -> referrer 映射;
  • 找直接推荐人:O(1) 查字典;
  • 找完整推荐链:while 一路往上走,注意检测环;
  • 找最终推荐人:在链路遍历过程中记住最后一个;
  • 批量查询时,加一层缓存,省掉大量重复遍历。

写完这些,小需求就从“业务逻辑里乱七八糟拼 SQL”变成了一个干净清晰的小模块,下次再有推荐人相关的东西,你直接把这几段 Python 拎出来改改就能用。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB