题目描述
思路解析动画文字版
看一眼这个矩阵:这是我们的例子。先肉眼找几条对角线感受一下:左上那条 1→1→1、它右边一条 2→2→2、左下那条 5→5。要判断的就是:是不是每条斜线都这样「一路同值」。
先想最直白的办法:最直白的想法:找出所有对角线的起点(第一行、第一列的格子),每条都顺着右下方走一遍,逐个比是不是相等。这能做,但要专门处理「哪些是起点、怎么往右下走」,写起来有点绕。
关键反转:换个角度:对角线就是「右下方向」,那么一个格子在它对角线上的前一个,就是它的左上角那格 (i-1, j-1)。所以只要每个格子都等于自己的左上角,整条对角线自然就全相等了——根本不用真的走斜线!
为什么这样就够了:如果 a 等于它的左上 b,b 又等于它的左上 c……那 a=b=c=… 整条线就都相等了。所以我们不必整条线一起看,只需逐格检查「我 == 我的左上角」。第一行、第一列没有左上角,直接跳过。
开始逐格检查 · 标出要查的格子:蓝色这些是第一行和第一列——它们没有左上角,不用检查(它们各自是某条对角线的起点)。真正要逐个验证的,是从 (1,1) 开始、行列都 ≥1 的格子。
检查 (1,1) · 对比左上 (0,0):看 (1,1) 这格(紫色),它的值是 1;它的左上角是 (0,0),值也是 1。1 == 1,相等,这格通过。
(1,1) 通过 · 锁定:(1,1) 检查通过,标成蓝色「已确认」。继续看它右边的格子。
检查 (1,2) · 对比左上 (0,1):看 (1,2),值是 2;它的左上角 (0,1) 也是 2。两格同时高亮(紫色)——2 == 2,相等,通过。
(1,2) 通过 · 锁定:(1,2) 也通过,变蓝锁定。已经确认了 2 格,继续往右看 (1,3)。
检查 (1,3) · 对比左上 (0,2):(1,3) 值 3,它的左上角 (0,2) 也是 3。3 == 3,通过。第二行三个待查格子全过了。
第二行检查完 · 全部锁定:第二行从 (1,1) 到 (1,3) 都通过了(蓝色)。注意我们一直只在比「自己 vs 左上角」,从没真的去走整条斜线,但效果完全一样。接着检查第三行。
检查 (2,1) · 对比左上 (1,0):进入第三行。(2,1) 值 5,它的左上角 (1,0) 也是 5。5 == 5,通过。
(2,1) 通过 · 锁定:(2,1) 变蓝锁定。这格落在左下那条 5→5 的对角线上,与上一格 (1,0) 的 5 一脉相承。继续看 (2,2)。
检查 (2,2) · 对比左上 (1,1):(2,2) 值 1,左上角 (1,1) 也是 1。1 == 1,通过。这一格刚好在那条「1→1→1」的主对角线上。
(2,2) 通过 · 锁定:(2,2) 锁定。只剩最后一格 (2,3) 还没查了。
检查 (2,3) · 对比左上 (1,2):最后一格 (2,3) 值 2,左上角 (1,2) 也是 2。2 == 2,通过。所有该查的格子都查完了。
全部通过 · 返回 true:6 个待查格子全部「等于自己的左上角」,没有任何一处不相等,所以这是托普利茨矩阵,返回 true。我们只扫了一遍矩阵就判完了。
回看 · 主对角线 1→1→1:回头看这条主对角线 (0,0)→(1,1)→(2,2),值是 1,1,1。我们之所以确定它全相等,靠的就是「(1,1)=左上(0,0)」和「(2,2)=左上(1,1)」这两次逐格比较串起来的。
回看 · 另一条对角线 2→2→2:再看 (0,1)→(1,2)→(2,3) 这条,值是 2,2,2,也全相等。每一条对角线都是被「相邻两格的逐个比较」悄悄验证过的——这就是这个技巧的妙处。
回看 · 左下角那条 5→5:(1,0)→(2,1) 这条短对角线值是 5,5,靠 (2,1)=左上(1,0) 这一次比较确认。短到只有两格的对角线,逻辑完全一样。
回看 · 角上的单格对角线 9 和 4:左下角的 9 和右上角的 4 各自是一条只有一个格子的对角线,无需比较、天然成立。这也解释了为什么第一行、第一列不用查——它们都是对角线的起点。
现在看反例:再看一个反例 [[1,2],[2,2]]。它的对角线 (0,0)→(1,1) 应该全相等才行。我们用同样的方法,逐格检查一下。
反例 · 检查 (1,1) 对比左上 (0,0):在 [[1,2],[2,2]] 里检查 (1,1),它的值是 2;它的左上角 (0,0) 是 1。2 ≠ 1,不相等!对角线 (0,0)→(1,1) 上出现了不一样的数(红色)。
反例 · 立刻返回 false:只要任意一格不等于它的左上角,就说明那条对角线不是同值,整个矩阵就不是托普利茨矩阵。立刻返回 false,根本不用再看后面的格子。
落到代码 · 一句话:代码极简:两层循环从 (1,1) 扫到右下角,发现任何一格不等于它的左上角就立即 return false;全都相等就 return true。第一行、第一列因为没有左上角,从下标 1 开始循环就自然跳过了。
本质金句:把「整条对角线都相等」这个全局条件,转化成「每个格子都等于自己左上邻居」这个局部条件——这是很多矩阵题的通用思路:用相邻关系替代远距离关系。
边界三连:单行、单列、单格这些情况下,根本没有「行列都 ≥1」的格子可比,循环一次都不进,直接返回 true,模板天然兜住,无需特判。
面试追问:最高频的追问是「一次只能读一行怎么办」——答出「只保留上一行、比 prev[j-1]」就能体现你对空间优化的敏感度。
参考代码
class Solution: def isToeplitzMatrix(self, matrix): m, n = len(matrix), len(matrix[0]) for i in range(1, m): for j in range(1, n): if matrix[i][j] != matrix[i - 1][j - 1]: return False return True复杂度
- 时间:O(m·n),每个格子最多比较一次,最坏扫完整张图
- 空间:O(1),只用几个下标变量,不开额外数组
易错点
面试追问把动画讲成自己的话
追问如果矩阵特别大、内存只能一次读一行(无法整体放进内存)怎么办?
追问为什么不需要单独处理第一行和第一列?
追问能用 (i-j) 是否相同来分组对角线吗?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
有效的数独
LeetCode 36 · 中等 · 沿着 矩阵套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题