Python技术迷

9月份一个同事被优化了,赔了N+1,找了三个月工作没有结果,刚刚知道他竟然去了蚂蚁,年包40多万,还涨了 30%~

刚看到个贴子,大概意思是:9月份一个同事被优化,拿了N+1,失业找了三个月工作一点动静没有,楼主还挺替她心疼。

结果今天一看朋友圈,人家入职蚂蚁了,年包四十多万,还比之前涨了30%,直接把楼主看羡慕了。

Image

这事最容易让人产生两种情绪:一是“她好牛啊”,二是“那我怎么还在原地踏步”。评论区肯定有人说是运气爆棚,也有人酸一句“这不就是幸存者效应”。但在我看来,能进蚂蚁这种级别的公司,光靠运气真不行,多半是之前能力就够,只是机会没来。

被优化那三个月,她大概率也不好受,只是我们看不到。

被裁不一定是终点,有时候是强制换道。我们羡慕别人的结果没问题,但更重要的是:别被比较气到,踏实把自己的那点本事攒厚了。

算法题:托普利茨矩阵

托普利茨矩阵这个事儿,说白了我是被面试题逼着学会的。

那天晚上快十一点,我还在公司楼下等外卖,手机一响,是我们组小李发的消息:“哥,面试又被问到托普利茨矩阵了,你到底咋记的?”我一边啃鸡腿一边给他语音讲,顺手就把代码敲了一版发过去,今天就按当时跟他唠嗑的方式跟你说一遍。

题目一般是这样的:给你一个二维矩阵 matrix,问你它是不是“托普利茨矩阵”(Toeplitz Matrix)。

托普利茨矩阵的定义就一句话:从左上到右下的每一条斜线(对角线),元素都一样。

举个例子就一下子明白了:

matrix = [
    [1, 2, 3, 4],
    [5, 1, 2, 3],
    [9, 5, 1, 2],
]

你顺着这些斜线看:

  • 1,1,1 在一条斜线上
  • 2,2,2 在一条斜线上
  • 3,3 在一条斜线上
  • 4 自己一条
  • 5,5 在一条
  • 9 自己一条

每条线上的数都没变,这就是托普利茨矩阵。

如果某个位置改成 8,比如 matrix[2][2] = 8,那个“1,1,8”这条斜线就坏掉了,直接不是托普利茨。

怎么把“看斜线”翻译成代码

人眼看是挨着斜着看,那代码里要不要真的一条条斜线去扫?当然可以,但是有个更偷懒的方法。

你看一下,同一条斜线上的元素,有个共同特点:

下标满足:(i, j) 和 (i-1, j-1) 在同一条斜线上。

所以只要这句话成立:

对于所有 i > 0 且 j > 0,都有matrix[i][j] == matrix[i-1][j-1]

那整个矩阵就是托普利茨。

所以我们其实就是:从第二行、第二列开始扫,如果发现有哪个格子和它左上角不一样,马上返回 False 就完事。

直接上最常用的 Python 写法

from typing import List

defis_toeplitz_matrix(matrix: List[List[int]]) -> bool:
# 空矩阵或者只有一行一列,怎么说都算“对”的
ifnot matrix ornot matrix[0]:
returnTrue

    rows = len(matrix)
    cols = len(matrix[0])

# 从第二行第二列开始扫
for i in range(1, rows):
for j in range(1, cols):
if matrix[i][j] != matrix[i - 1][j - 1]:
returnFalse

returnTrue

这个逻辑就非常直男: 一层 for 是行,一层 for 是列,遇到一个不对直接拍桌子走人。

时间复杂度:每个元素最多看一眼,O(m * n)。 空间复杂度:只用了几个变量,O(1)。

边界情况顺手说一下

写题的时候有几个小坑,面试官喜欢顺嘴问两句:

  1. 只有一行或者一列怎么办?一行你根本找不到“左上角”,条件里的循环本身就不会进,自然返回 True,本身没问题。

  2. 矩阵不规则行长不一样?正常算法题都会默认是“矩形矩阵”,也就是每行长度一样。要你自己防御的话,可以加个 assert 或者前面先统一检查一遍长度。

  3. 元素类型不是 int 呢?逻辑不受影响,只要支持 == 比较就行,字符串、bool 一样能玩。

矩阵特别大,内存放不下怎么办

有些题会再加一句:矩阵太大,不能一次性全部载入内存,只能一行一行地读。

这个时候,上面那种双层 for 就不太适合了,但我们刚刚的核心条件还是那句:

当前行和上一行,对齐错一列去比。

那就把矩阵换成“按行流式读”的版本,只要记住上一行就行了:

from typing import Iterable, List, Any

defis_toeplitz_stream(rows: Iterable[List[Any]]) -> bool:
"""
    rows 是一个“按行产生数据”的可迭代对象,
    比如:文件一行一行读进来。
    """

    prev_row = None

for row in rows:
if prev_row isnotNone:
# 当前行从第 1 列开始,对比上一行的前一列
for j in range(1, len(row)):
# 这里默认每行长度一样
if row[j] != prev_row[j - 1]:
returnFalse
        prev_row = row

returnTrue

这个版本有啥好处?

  • 内存里只保留两行
  • 逻辑还是那句:当前 == 上一行左边那一个
  • 很适合那种从文件、网络流里一点点读出来的场景

我之前排查 TCP 报文 1024 卡死那个 bug 时,分析数据包其实也是类似思路:一段一段看规律,再顺着规律去排异常。

再来一个构造托普利茨矩阵的小玩具

有时候写单测或者在本地玩一玩,会想自己生成一个托普利茨矩阵,这个也不难,规律就是:同一条斜线上值一样,而“斜线”可以用 i - j 来标记。

defbuild_toeplitz(diagonal_values, rows: int, cols: int):
"""
    diagonal_values: dict,key 是 (i - j),value 是该条斜线上的值
    比如:{0: 1, -1: 2, 1: 3}
    """

    matrix = [[0] * cols for _ in range(rows)]
for i in range(rows):
for j in range(cols):
            k = i - j
# 没给的斜线就随便填个默认值
            val = diagonal_values.get(k, 0)
            matrix[i][j] = val
return matrix

你随便传个 {0: 5, -1: 8},出来就是一个规规矩矩的托普利茨矩阵,拿去喂前面那个 is_toeplitz_matrix 校验,肯定是 True。

差不多就这样,托普利茨矩阵其实就是一句话的事儿: 记住“当前格子 = 左上角那个格子”,把这个条件写稳,再稍微考虑下大矩阵的场景,面试里就够用了。

行,我先去喝口水,你要是手上有具体题号或者你写的代码,可以丢过来,我帮你挑挑毛病。

-END-

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

🔥虎哥私藏精品🔥

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