Python技术迷

放弃华为16级,进了体制内一个非常清闲的部门,早八晚五,原以为会越来越好,结果发现工作没意义,工资也不好看,后悔了

刚看到个贴子:有网友说自己放弃华为16级,去了体制内清闲部门,本以为日子会越过越舒坦,结果发现工作没意义、工资也不好看,现在反而后悔了。

Image

在我看来,关键还是要想清楚自己要什么。有些人要稳定,有些人要成长,有些人要钱,没有哪个选择绝对好。怕就怕想着稳定又想要高薪,还希望工作有意义,这就容易失衡。

换个角度讲,体制内也不是不能发展,只是节奏慢、靠主动性,如果一味躺平,几年后自然会焦虑。

不过话说回来,工作嘛,不可能事事完美。选了哪条路,就尽量把路走通,别光盯着后悔。总的来说,找工作最重要的还是适合自己,踏实一点,路都会慢慢好起来。【备注:文末可领最新资料】

面试题:最大正方形

最大正方形这个题,大概意思就是:

给你一个只有 '0' 和 '1' 的二维数组(矩阵),'1' 表示这个格子是可以用的,'0' 表示不行。你要在里面找一块全是 1 的正方形,边长尽量大,最后返回这个正方形的面积(注意是面积,不是边长)。

比如有这么一块:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

里面最大的全 1 正方形是边长 2 的那块,所以答案是 2 * 2 = 4。

核心思路:别硬找,动规帮你算

暴力怎么干?穷举左上角、右下角,检查是不是正方形、是不是全 1……时间直接爆炸,面试官脸都黑了。

更聪明一点的办法,是给每个格子算一个东西:

以这个格子作为右下角,能形成的最大全 1 正方形,边长是多少?

我们定义一个 dp[i][j]:

  • 表示以 (i, j) 这个位置为右下角的,最大全 1 正方形的边长
  • 注意是“右下角”,这个很关键,不然状态转移不好写

那怎么推呢?

  • 直觉:我要在 (i, j) 这里形成一个更大的正方形,得看它的上边、左边、左上角这三个地方的情况

  • 如果 matrix[i][j] == '0',那这里根本没法做正方形,dp[i][j] = 0

  • 如果 matrix[i][j] == '1',那我能扩多大,取决于:

    • 上面 dp[i-1][j] 能有多高
    • 左边 dp[i][j-1] 能有多宽
    • 左上 dp[i-1][j-1] 能有多大正方形

你可以脑补个图:如果上面有一根“1 的柱子”、左边有一条“1 的横线”,但左上角那个位置撑不起那么大正方形,你也只能乖乖听左上角的。

所以有个非常经典的状态转移公式:

如果 matrix[i][j] == '1':
    dp[i][j] = min(
        dp[i-1][j],     上边
        dp[i][j-1],     左边
        dp[i-1][j-1]    左上
    ) + 1
否则:
    dp[i][j] = 0

为什么要取三个里的最小值? 你可以理解成:

三个人一起抬一个正方形,谁最弱,就按谁的极限来。

然后我们在遍历整个矩阵的过程中,顺便维护一个 max_side,记录所有 dp[i][j] 里的最大值,最后返回 max_side * max_side 就行了。

边界怎么办?

  • 第一行、第一列,没“左边”“上边”“左上角”可以看了

  • 但这些位置,如果自己是 '1',那它能形成的最大正方形边长只能是 1

  • 所以实现上可以:

    • 直接让 i、j 从 0 开始
    • 当 i == 0 或 j == 0 时,如果是 '1',就把 dp[i][j] = 1

先来一个看着最清晰、最好懂的二维 dp 写法:

from typing import List

classSolution:
defmaximalSquare(self, matrix: List[List[str]]) -> int:
ifnot matrix ornot matrix[0]:
return0

        rows, cols = len(matrix), len(matrix[0])
# 多加一行一列 0,可以少写一点边界判断
        dp = [[0] * (cols + 1) for _ in range(rows + 1)]
        max_side = 0

# 注意这里 i, j 从 1 开始,对应原矩阵的 i-1, j-1
for i in range(1, rows + 1):
for j in range(1, cols + 1):
if matrix[i - 1][j - 1] == '1':
                    dp[i][j] = min(
                        dp[i - 1][j],     # 上
                        dp[i][j - 1],     # 左
                        dp[i - 1][j - 1]  # 左上
                    ) + 1
                    max_side = max(max_side, dp[i][j])
# 是 '0' 的话 dp[i][j] 默认就是 0,不用管

return max_side * max_side

简单捋一下:

  • 时间复杂度:O(m * n),m 行 n 列,每个格子就看一眼
  • 空间复杂度:O(m * n),用了一个同尺寸的 dp 矩阵(加了一行一列,不过量级一样)

这个代码在面试里已经完全够用了,可读性很好。

如果你想装一装“空间优化”,也可以把 dp 从二维压成一维。 观察转移式:

dp[i][j] 来自:
    上:    dp[i-1][j]
    左:    dp[i][j-1]
    左上:  dp[i-1][j-1]

如果我们一行一行扫,其实:

  • dp[j] 可以表示“当前行的第 j 列,对应的 dp 值”
  • 那“上边”的值就是更新之前的 dp[j]
  • “左边”就是 dp[j-1]
  • “左上”我们就用一个变量 prev 暂存每次更新前的 dp[j]

写出来大概是这样:

from typing import List

classSolution:
defmaximalSquare(self, matrix: List[List[str]]) -> int:
ifnot matrix ornot matrix[0]:
return0

        rows, cols = len(matrix), len(matrix[0])
        dp = [0] * (cols + 1)
        max_side = 0

for i in range(1, rows + 1):
            prev = 0# 对应左上角 dp[i-1][j-1]
for j in range(1, cols + 1):
                temp = dp[j]  # 还没更新前的 dp[j] = 上边
if matrix[i - 1][j - 1] == '1':
                    dp[j] = min(dp[j], dp[j - 1], prev) + 1
                    max_side = max(max_side, dp[j])
else:
                    dp[j] = 0
                prev = temp  # 下一轮用作左上角

return max_side * max_side

这个版本:

  • 时间还是 O(m * n)
  • 但空间变成了 O(n),只用了一行 dp 数组+几个变量

如果你在面试里能先写出二维版,讲清楚含义,再说一句“其实可以空间压缩成一维,思路是这样的……”,基本上这一题就拿满分了。

差不多就这样,一个“最大正方形”就吃透了。你要是愿意,可以顺手把“最大矩形”、“全 1 矩形”那俩题一起刷了,思路串一串会更爽。

-END-

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

🔥虎哥私藏精品🔥

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