老公不想工作了咋办?老公做Java开发,在杭州工作7年了,房贷还有15年不到,月供5000多,有个上幼儿园的女儿~
刚看到个贴子,说一女生吐槽:老公在杭州做了7年Java,房贷15年、月供5000多,女儿上幼儿园了,结果老公突然表示“不想工作了”。
网友们有的劝赶紧离婚,有的说让老公休息一阵,还有人站队老公,觉得程序员压力太大。我觉得这事吧,先别急着扣“渣男”或“扶不起”这样的帽子,关键是搞清楚,他是不想干这份工作,还是不想再承担家庭责任了。
如果是职业倦怠,可以一起算笔账:房贷、孩子、日常开支能撑多久?在安全边界内给他一段时间缓冲,允许转行、降薪,甚至先找份轻松点的糊口工作,而不是直接躺平在家打游戏。
但如果他只想享受家庭红利,不愿再付出,那就要让他明白:婚姻是合伙公司,不是养老院。
算法题:寻找峰值
昨天晚上十一点多我还在公司楼下晃悠,拿着杯一点点发呆,我们组那个小李突然微信我一句:哥,数组里那个“寻找峰值”的题你咋写的,我写完总感觉不对劲。 我当时脑子一懵,本来准备打车回家的,结果又坐楼下椅子上给他语音讲了半天……
先说人话版是啥意思哈。 所谓“峰值”,就是数组里某个位置 i,满足:
它比左边大 也比右边大 边界有点特殊:最左边没左邻居,就只要比右边大就行;最右边同理。题目一般还会说一个条件:相邻两个数不相等,这个很关键,不然逻辑会乱。
举个特别随便的例子:[1, 3, 2, 4, 1]这里 3 是峰值(因为 3 > 1 且 3 > 2),4 也是峰值(4 > 2 且 4 > 1),你随便找一个峰值下标返回都行。
小李一开始写的是最直接那个办法,他说“我就从头扫到尾,一个个比不就完了嘛”。 其实也没毛病,这就是最暴力但是最稳的方式:
deffind_peak_linear(nums):
n = len(nums)
if n == 1:
return0
for i in range(n):
left_ok = (i == 0) or (nums[i] > nums[i - 1])
right_ok = (i == n - 1) or (nums[i] > nums[i + 1])
if left_ok and right_ok:
return i
# 理论上不会走到这里,因为按照题目条件一定有峰值
return-1
这个时间复杂度就是 O(n),数组多长你就最多走几步。日常写业务代码,这种方案八成就直接提测了,对吧。
不过面试官一般会追问一句:能不能再快点? 你要是说“不能”,那后面可能就没后面了……
我当时就问小李:你爬山的时候,是从山脚一个一个台阶往上试的吗? 肯定不是嘛,你大概率会找个看起来更高的方向走,对吧。
“寻找峰值”这题其实可以用 二分 做成 O(log n) 的复杂度,思路特别像爬山找最高点,但不是找“最高”,而是找一个“局部更高”的那个点。
核心想法是这样的(我尽量说得口水一点):
取中间位置
mid比较
nums[mid]和右边nums[mid + 1]
如果 nums[mid] < nums[mid + 1],说明右边在往上爬,还没到头,那一定能在右边区间[mid+1, right]找到一个峰如果 nums[mid] > nums[mid + 1],说明右边是在往下走,峰值在左边一块[left, mid]肯定存在
每次这么一砍,区间缩小一半,最后 left == right,这个位置就是一个峰值下标
听着有点抽象,小李当时也是一脸懵,我就随手写了个 Python 代码发给他:
deffind_peak(nums):
"""
二分法寻找任意一个峰值下标
:param nums: List[int]
:return: int
"""
left, right = 0, len(nums) - 1
while left < right:
mid = (left + right) // 2
# 如果中间这个点比右边小,说明峰值在右边
if nums[mid] < nums[mid + 1]:
left = mid + 1
else:
# 否则峰值一定在左边(包含 mid)
right = mid
# 循环结束时,left == right,就是峰值下标
return left
这段其实就是标准答案级别的写法了,面试的时候背下来都不过分。
小李当时问了一个特别典型的问题: “为啥比较的是 mid 和 mid+1,不是 mid-1 呢?”
其实随便一边都行,只要你逻辑自洽。 我们现在这版是看“右边是不是在往上”,如果你喜欢从左边判断,也能写成“右边是不是在往下”,本质是一样的。 关键点是:由于题目保证相邻不相等,所以只会有“严格上升”或者“严格下降”,不会有平的那种尴尬情况。
还有几个常见坑,小李都踩了:
第一个是数组长度为 1 的时候。 这时候你如果在 while 里写 mid + 1,没把循环条件写好,就直接越界。 上面那种写法 left < right 就比较安全,n == 1 时压根进不了循环,直接返回 0,下标没问题。
第二个是单调数组: 比如 [1, 2, 3, 4, 5],峰值其实就是最后一个; 再比如 [5, 4, 3, 2, 1],峰值就是第一个。 二分这个写法也能自动处理好,不用你额外判断,很省心。
有人群里问我:要不要写个简单测试? 那就顺手整个最简陋的版,跑一下心里踏实点:
deftest_find_peak():
cases = [
[1],
[1, 2],
[2, 1],
[1, 3, 2, 4, 1],
[1, 2, 3, 4, 5],
[5, 4, 3, 2, 1],
]
for nums in cases:
idx = find_peak(nums)
print(nums, "-> index:", idx, "value:", nums[idx])
if __name__ == "__main__":
test_find_peak()
你本地跑一下,大概打印出来就是每组都有个还算“高”的点,位置可能不一样,但都符合峰值定义。
我当时跟小李说,这题真正考的不是代码,而是两个习惯:
一个是:遇到“找某个位置”就先想想能不能用二分,哪怕一开始想不清楚,也值得琢磨一下,因为这种思路太常用了; 另一个是:先写一个又蠢又对的版本(线性扫描),再慢慢想怎么优化,而不是一上来就硬刚高难度写法。
行,我这会儿也困得不行了,先去泡杯茶,你要还有啥题,丢过来一起唠。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB