Python技术迷

面一个90年的经理岗,开口要40k+,本来聊得挺顺的。HR总监突然灵魂发问:你这年纪主动离职够有勇气啊。候选人苦笑:公司6个月没发工资了

刚看到个贴子,说有个90年的去面经理岗,开口40k+,前面聊得挺好,结果HR总监来一句:“这年纪主动离职挺有勇气。”候选人苦笑,说原公司已经6个月没发工资了,不走难道喝西北风。

Image

网友们的回复我看了看,有人吐槽候选人要价高不自量力,也有人骂HR站着说话不腰疼,拖欠工资还谈忠诚。怎么说呢,我觉得这事首先得分清:半年没工资,离职不是“勇气”,是基本求生反应;其次,40k贵不贵,看的是能力、岗位价值和行业行情,跟年龄、听不听话真没多大关系。真正离谱的,是那个拖薪还想着员工死心塌地的公司。

从我的角度看,职场别老拿“稳定、忠诚”PUA人。公司可以算成本,打工人也有权算现金流。

力

算法题:区间子数组个数

有个整数数组 nums,再给你两个整数 left 和 right。 要数一数:有多少个连续子数组,它们里面的最大值刚好落在 [left, right] 这个区间里。

举个很经典的例子:

nums = [2, 1, 4, 3]
left, right = 2, 3

符合要求的子数组有三个:

  • [2] 最大值 2 在区间里
  • [2, 1] 最大值还是 2
  • [3] 最大值 3 在区间里

所以答案是 3。

听起来挺简单,对吧?但直接暴力搞,其实挺容易超时。

最直接的思路:

  1. 枚举所有子数组(双层循环,左边界 i,右边界 j)
  2. 每次顺便算一下这段里最大的值
  3. 看这个最大值在不在 [left, right],在就计数 +1

伪代码大概这样:

ans = 0
for i in range(n):
    cur_max = nums[i]
for j in range(i, n):
        cur_max = max(cur_max, nums[j])
if left <= cur_max <= right:
            ans += 1

问题也很明显: 数组长度如果是 1e5 级别,这就是 O(n²),直接寄。

那怎么把它变成 O(n) 呢?这题其实有个非常巧的小套路。

关键观察:先数“最大值 ≤ 某个值”的子数组

我们先别死盯着 [left, right],把问题拆一下。

定义一个函数 count_leq(bound): 统计最大值 ≤ bound 的子数组个数。

然后你会发现一个很妙的式子:

最大值在 [left, right] 的个数 = 最大值 ≤ right 的个数 − 最大值 ≤ left - 1 的个数

为啥对?

  • “最大值 ≤ right” 里面,既包括最大值在 [left, right] 的,也包括最大值 < left 的
  • “最大值 ≤ left - 1” 只包括最大值 < left 的
  • 一减,就把“太小的那些”减掉了,剩下的就是我们要的那一批

所以整个题目就变成了:如何 O(n) 算出 count_leq(bound)?

单次扫描搞定 count_leq(bound)

这个函数的思路其实很朴素:

从左往右扫一遍数组,维护一个变量 cur —— 表示以当前下标结尾、且最大值 ≤ bound 的连续子数组有多少个。

规则只有两条:

  1. 当前元素 x 如果 x <= bound:

  • 那它可以和前面那些“合法子数组”接上
  • 也可以单独成一个新的子数组
  • 所以 cur += 1
  • 当前元素 x 如果 x > bound:

    • 那所有包含它的子数组最大值都 > bound,不合法
    • 以它结尾的合法子数组数量变成 0
    • 所以 cur = 0

    每走一步,把 cur 累加进 ans 就行了,因为:

    对于每个位置 i, “以 i 结尾的合法子数组个数”就是 “以 i 结尾刚刚新增的那批子数组数量”。

    代码长这样:

    defcount_leq(nums, bound):
        ans = 0
        cur = 0
    for x in nums:
    if x <= bound:
                cur += 1# 新增的合法子数组,都以当前 x 结尾
    else:
                cur = 0# 被一个大于 bound 的数打断
            ans += cur
    return ans

    整个过程只扫一遍数组,所以是 O(n),而且只用常数空间。

    上面都串起来,就是完整答案了,用 Python 写得干净一点:

    from typing import List

    defcount_leq(nums: List[int], bound: int) -> int:
    """
        统计 nums 中,最大值 <= bound 的连续子数组个数
        """

        ans = 0
        cur = 0
    for x in nums:
    if x <= bound:
                cur += 1
    else:
                cur = 0
            ans += cur
    return ans


    defnum_subarray_bounded_max(nums: List[int], left: int, right: int) -> int:
    """
        返回最大值在 [left, right] 区间内的子数组个数
        """

    # 最大值 <= right 的子数组数目
        total_leq_right = count_leq(nums, right)
    # 最大值 <= left - 1 的子数组数目(也就是最大值 < left)
        total_lt_left = count_leq(nums, left - 1)
    # 两者相减,剩下的就是最大值在 [left, right] 的
    return total_leq_right - total_lt_left


    if __name__ == "__main__":
        nums = [2, 1, 4, 3]
        left, right = 2, 3
        print(num_subarray_bounded_max(nums, left, right))  # 输出 3

    复杂度简单算一下:

    • count_leq 是 O(n),
    • 调用两次还是 O(n),
    • 空间 O(1)。

    就这样,这题思路其实就两步: 先学会数“最大值 ≤ 某个值”的子数组,再用一个减法把区间卡出来。 你可以自己随便再写几个测试用例试试,比如数组里全都小于 left、或者全都大于 right,看结果是不是合理。

    -END-

    我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

    🔥虎哥私藏精品🔥

    虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB