老婆在成都,我在广州,为了结束异地,放弃小鹏高级经理职位,降级去成都要少20万,值得吗?
看到一哥们差点把自己“职业路线”给跑偏了:老婆在成都,他在广州,想结束异地,准备把小鹏高级经理的位置放下,降级去成都,年薪少个20万,问值不值。
程序员看问题喜欢算账:20万是钱,也是压力测试。你在广州赚得多,但两地分居的成本更隐形——机票、高铁、请假、情绪、吵架后的冷战时间,这些都不开发票。
真想去成都,先把条件谈明白:岗位成长、回升通道、住房与家庭分工。钱少点不一定亏,日子顺了,才像把系统从“内存泄漏”修好。
算法题:非递减数列
那个…非递减数列这个题啊,我是有心理阴影的。之前晚上十点准备关电脑打游戏,一个同事在群里来一句:“东哥,数组这题我老是WA,你帮我看一眼?”结果一眼看到了半夜十二点,游戏没打成,Bug还在那儿顽强地跑。
先说人话版啊,非递减数列是啥意思?就是数组 nums 里,从左到右不下降:nums[i] <= nums[i+1],对吧。题目一般还要加个限制:最多改一个元素,问你能不能把整个数组改成非递减的。
听起来像什么?像你有一串同事的绩效分,领导说:你最多帮一个人“润色”一下评分,看能不能做到后面的人不要比前面的人高太多…咳,打个比方。
我当时同事给的那个数组是这样:[4, 2, 3]。肉眼一看就知道,把 4 改成 2 或 1 啥的,就变成 [2,2,3],不就非递减了嘛,so easy。 但问题是,写代码的时候,脑子一紧张,就会开始瞎改,比如有的人直接: 发现 nums[i] > nums[i+1] 就把 nums[i] = nums[i+1],然后就寄了。
我当时跟他说,你别急,咱先想清楚“冲突”出在哪。所谓冲突就是: 只要出现了 nums[i] > nums[i+1],这地方肯定要动手脚,要么改左边,要么改右边,而且这种冲突最多只能出现一次,不然你改一回也救不回来。
思路其实就一条线:
从左到右扫一遍数组 每次发现 nums[i] > nums[i+1],记一下“我已经改过一次了”如果是第二次发现这种情况,那就直接返回 False 第一次冲突时,再想一想是该改前面的,还是改后面的
那个关键的第 4 步,很多人容易写糊。你想象一下几种情况:
如果冲突发生在最开头 i == 0,那好说,前面没人,随便你改左还是改右,一般我就偷懒,直接把nums[i]变小,等于nums[i+1]。如果 nums[i-1] <= nums[i+1],说明把nums[i]变成nums[i+1]也是安全的,不会破坏前面的顺序。否则,只能把右边的 nums[i+1]变大,等于nums[i],让它别比前面小。
当时我边跟他语音解释,边随手敲了个 Python 小函数,大概长这样:
defcan_be_non_decreasing(nums):
"""
判断一个数组能不能通过最多修改一个元素,
变成非递减数列
"""
changed = False# 标记有没有动过手
for i in range(len(nums) - 1):
if nums[i] <= nums[i + 1]:
continue
# 走到这里说明出现 nums[i] > nums[i+1]
if changed:
# 已经改过一次了,还撞上这种情况,直接GG
returnFalse
changed = True
# 决定改左边还是改右边
if i == 0or nums[i - 1] <= nums[i + 1]:
# 改左边更安全:让当前值降下去
nums[i] = nums[i + 1]
else:
# 否则只能把右边抬上来
nums[i + 1] = nums[i]
returnTrue
这个代码有两个小细节,别看着像废话,其实都是亲身踩坑换来的:
一个是那个 changed 标记,一定要有。你想啊,题目说“最多改一个”,你如果不记一下,循环里发现一次就改一次,那就是“疯狂整容”,直接违规。
另一个是不要一上来就无脑改左边。我当时同事写的是这样:
if nums[i] > nums[i+1]:
if changed:
returnFalse
changed = True
nums[i] = nums[i+1]
然后数组 [3, 4, 2, 3] 直接翻车。你可以自己手算下:
下标 1 和 2 冲突:4 > 2,他把 4 改成 2,数组变成 [3, 2, 2, 3]结果 3 > 2,又冲突一次,挂了
但实际上这题的正确操作是:把 nums[2] 改成 4,变成 [3, 4, 4, 3],然后再看最后一对,还是有问题…哦对,这个例子本来就应该是 False,我嘴快了,你自己再试几个就明白为啥那个逻辑不稳了。
我后来跟他说,写这种“最多改一次”的题,有个通用小套路:
用一个布尔变量记有没有动刀 每次看到不合法的地方,先想想“能不能只通过这一刀解决问题” 如果发现第二个不合法地方,就可以直接返回 False,别犹豫
还有人问,那我能不能不修改原数组?比如有的场景你不想动原始数据,那就简单粗暴一点,先复制一份:
defcan_be_non_decreasing_safe(nums):
arr = nums[:] # 浅拷贝一份
return can_be_non_decreasing(arr)
当然实际做题一般不这么矫情,直接改原数组就完事了,面试官也不会在意你那点小改动。
写完给他跑了几个例子:
print(can_be_non_decreasing([4, 2, 3])) # True
print(can_be_non_decreasing([3, 4, 2, 3])) # False
print(can_be_non_decreasing([1, 2, 3, 3, 3])) # True
结果他在群里来一句:“东哥,这不就是扫一遍加个标记吗?好简单哦。” 我当时整个人都无语了,你们知道吧,这种话听着就很想把刚才的代码删了重写一遍,再顺手关电脑睡觉。
反正这个题你记住两点就行: 一个是只允许一处“逆序”,多了必挂; 另一个是碰到“逆序”,先别动手,脑子里先想一下“改左还是改右更安全”,就这点“多想一秒”的功夫,能少很多 Bug。
行了不说了,我去给自己泡个咖啡,你要是把你的写法丢给我,我顺手帮你挑挑毛病,顺便看还能不能优化一行。