Python技术迷

杀人诛心!推荐的面试候选人,连一道最基础的“三数求和(3 Sum)”题都无法完成。因此,我们决定也为你重新安排一次技术面试

给我看得一愣一愣的。 有网友说,公司面试里出了道很基础的 3 Sum,结果候选人没做出来。正常来说,这事也就到这儿了,最多就是候选人没过,对吧? 结果更狠的来了。 公司反手把推荐人也叫出来了:你推荐的人连这题都写不明白,那我们是不是也得重新看看你的技术水平?
这就有点杀人诛心了。 候选人挂了,推荐人跟着被质疑,连判断力、筛人标准、技术资历一起被翻出来晒。打工人看完血压都上来了。 说白了,内推这玩意儿,本来是帮人开个门,结果一不小心,门没开成,自己还被拉进去补考。HR看了沉默,程序员看了更沉默。
今日算法题 评分数组一摆出来,糖果就不能平均发了。 比如这一组:
ratings = [1, 3, 2, 2, 1]
每个孩子至少一颗。评分比旁边高的,糖果也得比旁边多。这个题最容易写歪的地方,不是条件看不懂,而是你只从左往右扫一遍。 我第一眼看到这种题,一般不急着上复杂结构。先拿最小约束压一下:每个人先发 1 颗。
candies = [1] * len(ratings)
然后看左边。 如果当前孩子评分比左边高,那当前糖果必须比左边多一颗。
for i in range(1, len(ratings)):
    if ratings[i] > ratings[i - 1]:
        candies[i] = candies[i - 1] + 1
这一步扫完,[1, 3, 2, 2, 1] 会变成:
[1, 2, 1, 1, 1]
看着好像没问题,其实右边关系还没处理。 位置 2 的评分是 2,右边位置 3 也是 2,不用管。位置 3 的评分 2 比位置 4 的评分 1 高,所以位置 3 的糖果必须比位置 4 多。 这就是为什么还得从右往左再扫一遍。
for i in range(len(ratings) - 2, -1, -1):
    if ratings[i] > ratings[i + 1]:
        candies[i] = max(candies[i], candies[i + 1] + 1)
这里的 max 不能省。 这个地方我见过不少代码直接写成:
candies[i] = candies[i + 1] + 1
这就有点莽了。因为左边那一轮已经处理过“比左边高”的约束,你现在直接覆盖,可能把前面刚满足的关系又打坏。 完整代码我一般会写成这样:
def min_candy(ratings):
    ifnot ratings:
        return0

    n = len(ratings)
    candies = [1] * n

    # 先管左邻居:右边评分高,就比左边多拿
    for i in range(1, n):
        if ratings[i] > ratings[i - 1]:
            candies[i] = candies[i - 1] + 1

    # 再管右邻居:左边评分高,就至少比右边多拿
    for i in range(n - 2, -1, -1):
        if ratings[i] > ratings[i + 1]:
            need = candies[i + 1] + 1
            if candies[i] < need:
                candies[i] = need

    return sum(candies)


print(min_candy([1, 3, 2, 2, 1]))  # 7
print(min_candy([1, 2, 2]))        # 4
print(min_candy([5, 4, 3, 2, 1]))  # 15
这个题其实就两个方向的约束。 左边约束,左到右扫。 右边约束,右到左扫。 最后把每个位置需要的最小糖果数加起来就行。 时间复杂度是 O(n) ,数组扫两遍。空间复杂度是 O(n) ,用了一个糖果数组。 要是面试官继续追问能不能优化空间,那是另一个写法,用上升坡、下降坡去算。但我一般不建议一上来就写那个,边界太容易绕晕。这个双向扫描版本,思路稳,代码也不容易出错。