766. 托普利茨矩阵
问题描述
给你一个 m x n
的矩阵 matrix
。如果这个矩阵是托普利茨矩阵,返回 true
;否则,返回 false
。
如果矩阵中每一条由左上到右下的对角线上的元素都相同,那么这个矩阵是托普利茨矩阵。
示例:
输入: matrix = [[1,2,3,4],[5,1,2,3],[9,5,1,2]]
输出: true
解释:
在上述矩阵中, 其对角线为:
"[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]"。
各条对角线上的所有元素都相同, 因此答案是 True 。
输入: matrix = [[1,2],[2,2]]
输出: false
解释:
对角线 "[1, 2]" 上的元素不同。
算法思路
对角线遍历法:
- 对于矩阵中的每个元素(除了最后一行和最后一列),检查其右下角元素是否相等
- 即检查
matrix[i][j] == matrix[i+1][j+1]
- 如果所有检查都通过,则是托普利茨矩阵
核心思想:托普利茨矩阵的定义是每条对角线上的元素相同,这等价于每个元素都等于其右下角的元素。
代码实现
方法一:逐元素检查法(推荐解法)
class Solution {
/**
* 判断矩阵是否为托普利茨矩阵
*
* @param matrix m x n 的整数矩阵
* @return 如果是托普利茨矩阵返回true,否则返回false
*/
public boolean isToeplitzMatrix(int[][] matrix) {
// 输入校验
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return true; // 空矩阵认为是托普利茨矩阵
}
int m = matrix.length; // 行数
int n = matrix[0].length; // 列数
// 特殊情况:单行或单列矩阵
if (m == 1 || n == 1) {
return true;
}
// 遍历矩阵中除最后一行和最后一列外的所有元素
for (int i = 0; i < m - 1; i++) {
for (int j = 0; j < n - 1; j++) {
// 检查当前元素是否等于其右下角元素
if (matrix[i][j] != matrix[i + 1][j + 1]) {
return false;
}
}
}
// 所有检查通过,是托普利茨矩阵
return true;
}
}
方法二:对角线遍历法
class Solution {
/**
* 按对角线遍历的解法
* 逻辑更贴近问题定义
*
* @param matrix 输入矩阵
* @return 是否为托普利茨矩阵
*/
public boolean isToeplitzMatrix(int[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return true;
}
int m = matrix.length;
int n = matrix[0].length;
if (m == 1 || n == 1) {
return true;
}
// 从第一行开始,遍历每条对角线
for (int j = 0; j < n; j++) {
if (!isValidDiagonal(matrix, 0, j)) {
return false;
}
}
// 从第二行开始(第一行已处理),遍历每条对角线
for (int i = 1; i < m; i++) {
if (!isValidDiagonal(matrix, i, 0)) {
return false;
}
}
return true;
}
/**
* 检查从 (row, col) 开始的对角线是否所有元素相同
*/
private boolean isValidDiagonal(int[][] matrix, int row, int col) {
int m = matrix.length;
int n = matrix[0].length;
int value = matrix[row][col];
while (row < m && col < n) {
if (matrix[row][col] != value) {
return false;
}
row++;
col++;
}
return true;
}
}
算法分析
-
时间复杂度:O(m × n)
- 需要遍历矩阵中除边界外的所有元素
- 每个元素的检查是 O(1)
-
空间复杂度:O(1)
- 只使用常数额外空间
-
算法特点:
- 直接检查相邻关系
- 一次遍历解决问题
- 代码简洁高效
算法过程
matrix = [[1,2,3,4],[5,1,2,3],[9,5,1,2]]
:
- i=0, j=0:
matrix[0][0]=1
,matrix[1][1]=1
- i=0, j=1:
matrix[0][1]=2
,matrix[1][2]=2
- i=0, j=2:
matrix[0][2]=3
,matrix[1][3]=3
- i=1, j=0:
matrix[1][0]=5
,matrix[2][1]=5
- i=1, j=1:
matrix[1][1]=1
,matrix[2][2]=1
- i=1, j=2:
matrix[1][2]=2
,matrix[2][3]=2
- 所有检查通过,返回 true
测试用例
public static void main(String[] args) {
Solution solution = new Solution();
// 测试用例1:标准托普利茨矩阵
int[][] matrix1 = {{1,2,3,4},{5,1,2,3},{9,5,1,2}};
System.out.println("Test 1: " + solution.isToeplitzMatrix(matrix1)); // true
// 测试用例2:非托普利茨矩阵
int[][] matrix2 = {{1,2},{2,2}};
System.out.println("Test 2: " + solution.isToeplitzMatrix(matrix2)); // false
// 测试用例3:单行矩阵
int[][] matrix3 = {{1,2,3,4}};
System.out.println("Test 3: " + solution.isToeplitzMatrix(matrix3)); // true
// 测试用例4:单列矩阵
int[][] matrix4 = {{1},{2},{3}};
System.out.println("Test 4: " + solution.isToeplitzMatrix(matrix4)); // true
// 测试用例5:1x1矩阵
int[][] matrix5 = {{5}};
System.out.println("Test 5: " + solution.isToeplitzMatrix(matrix5)); // true
// 测试用例6:空矩阵
int[][] matrix6 = {};
System.out.println("Test 6: " + solution.isToeplitzMatrix(matrix6)); // true
// 测试用例7:2x2托普利茨矩阵
int[][] matrix7 = {{1,2},{3,1}};
System.out.println("Test 7: " + solution.isToeplitzMatrix(matrix7)); // true
// 测试用例8:3x3非托普利茨矩阵
int[][] matrix8 = {{1,2,3},{4,5,6},{7,8,9}};
System.out.println("Test 8: " + solution.isToeplitzMatrix(matrix8)); // false
}
关键点
-
对角线特性:
- 每条对角线上的元素满足
matrix[i][j] == matrix[i+1][j+1]
- 这是托普利茨矩阵的核心性质
- 每条对角线上的元素满足
-
遍历范围:
- 只需遍历
[0, m-2] × [0, n-2]
的子矩阵 - 边界元素无需检查
- 只需遍历
-
边界处理:
- 空矩阵
- 单行/单列矩阵
- 1x1矩阵
-
算法效率:
- 无法进一步优化时间复杂度
- 必须检查所有相关元素
常见问题
-
为什么检查右下角就够了?
- 因为如果每个元素都等于其右下角元素
- 那么整条对角线上的元素都相等
-
如何处理负数?
- 算法同样适用
- 数值比较与正负无关
-
能否用哈希表?
- 可以,但过于复杂
- 不如直接比较高效