Python技术迷

领导的嫡系捅了个大篓子,出了严重bug,甲方追着要人背锅。领导找我谈话,说综合考虑之后决定让我来顶这个锅。

这领导算盘珠子都崩我脸上了。

嫡系捅了大篓子,严重 bug 出来,甲方开始追责,领导不去查谁写的、谁评审的、谁上线的,转头找一个没参与过需求的人谈话,说什么“综合考虑”,决定让你来背。

这哪叫综合考虑啊,这叫挑软柿子。

Image

这种事千万别一上头就拍桌子,也别傻乎乎点头。先把边界划清楚:这个需求什么时候立项、谁开发、谁测试、谁上线、你有没有参与记录,全部拉出来。聊天记录、邮件、工单、代码提交,能留的都留。

领导私下让你认,你就别私下认。让他走正式流程,让他说清楚依据。真要你签字、写说明,更要谨慎,别写成“我负责”。可以配合排查问题,但背锅是另一回事。

帮忙可以,救火也行,但没干过的事,别认。领导脸面重要,你饭碗也不是纸糊的。

今日算法题

数组明明只乱了一小段,结果有人上来就把整个数组排一遍。

能过。

但我第一眼不太喜欢这个写法。因为这题问的是“最短无序连续子数组”,不是问你“排完以后长什么样”。排序再对比,等于把现场全推平了,再回头找事故范围。

题目大概是这样:

nums = [2, 6, 4, 8, 10, 9, 15]

只要把中间这段:

[6, 4, 8, 10, 9]

排好,整个数组就有序了,所以答案是 5。

这题真正要抓的东西,其实是两个边界。

左边界怎么来?

从左往右扫,正常递增数组里,当前值应该不小于前面见过的最大值。

一旦出现:

nums[i] < max_seen

说明当前位置这个数,被前面某个更大的数“压住”了,它肯定落在无序区间里。

比如扫到 4 的时候,前面最大值是 6,4 < 6,这里就不对劲。

再扫到 9 的时候,前面最大值已经是 10,9 < 10,这里也不对劲。

所以右边界会不断往后推。

右边界知道了,左边界反过来找。

从右往左扫,正常递增数组里,当前值应该不大于右边见过的最小值。

一旦出现:

nums[i] > min_seen

说明当前位置这个数太大了,挡住了右边更小的数,它也得进无序区间。

代码我一般会这么写,不花里胡哨:

defshortest_unsorted_part(nums: list[int]) -> int:
    n = len(nums)
if n < 2:
return0

    right = -1
    max_seen = nums[0]

for i in range(1, n):
if nums[i] < max_seen:
            right = i
else:
            max_seen = nums[i]

if right == -1:
return0

    left = 0
    min_seen = nums[-1]

for i in range(n - 2, -1, -1):
if nums[i] > min_seen:
            left = i
else:
            min_seen = nums[i]

return right - left + 1

拿刚才那个数组跑一下:

nums = [2, 6, 4, 8, 10, 9, 15]
print(shortest_unsorted_part(nums))

输出:

5

这里有个小坑,别把边界判断写反。

从左往右扫,不是遇到下降就结束。因为后面可能还有更远的位置也被前面的最大值影响。

看这个数组:

[1, 3, 5, 4, 2, 6]

你看到 4 < 5,右边界先到 3。

但继续扫到 2,它也小于前面的最大值 5,所以右边界还得往后挪到 4。

这就是为什么代码里不是 break,而是继续扫完整个数组。

反方向也一样。

nums = [1, 3, 5, 4, 2, 6]
print(shortest_unsorted_part(nums))  # 4

需要排序的是:

[3, 5, 4, 2]

长度就是 4。

这题如果用排序对比,时间复杂度是 O(n log n)。不是不能写,面试里也不会说你错。

但这个双向扫描是 O(n),只用几个变量。更重要的是,它没有绕路。

左扫抓“被前面大数污染的位置”。

右扫抓“挡住后面小数的位置”。

两个边界一合,答案就出来了。数组本来有序时,左扫阶段 right 一直是 -1,直接返回 0,也不用额外补什么特殊逻辑。