杀人诛心!推荐的面试候选人,连一道最基础的“三数求和(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)
,用了一个糖果数组。
要是面试官继续追问能不能优化空间,那是另一个写法,用上升坡、下降坡去算。但我一般不建议一上来就写那个,边界太容易绕晕。这个双向扫描版本,思路稳,代码也不容易出错。