矩阵(4) --hot 100 in leetcode
矩阵置零 算法概述 原题
题目要求为将矩阵中0元素的同行同列都转化为0。避免不了矩阵每个元素都要遍历一次,没有什么优化空间,但是空间复杂度上可以通过使用矩阵内部的空间用以标记减少空间消耗。在这里,使用了第一行第一列作为需要置零的行列标记,但实际上第一行置零与否也被包含在第一列的标记内,所以只需创造标记之后,将对第一行的遍历放在最后(第一行是否需要遍历只看[0][0]),第一列是否置零用另外的标记变量标记。
时间复杂度为O(mn):遍历矩阵
空间复杂度为O(1):一个标记变量
JAVA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution { public void setZeroes (int [][] matrix) { int m = matrix.length, n = matrix[0 ].length; boolean flagCol0 = false ; for (int i = 0 ; i < m; i++) { if (matrix[i][0 ] == 0 ) { flagCol0 = true ; } for (int j = 1 ; j < n; j++) { if (matrix[i][j] == 0 ) { matrix[i][0 ] = matrix[0 ][j] = 0 ; } } } for (int i = m - 1 ; i >= 0 ; i--) { for (int j = 1 ; j < n; j++) { if (matrix[i][0 ] == 0 || matrix[0 ][j] == 0 ) { matrix[i][j] = 0 ; } } if (flagCol0) { matrix[i][0 ] = 0 ; } } } }
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Solution {public : void setZeroes (vector<vector<int >>& matrix) { int m = matrix.size (); int n = matrix[0 ].size (); int flag_col0 = false ; for (int i = 0 ; i < m; i++) { if (!matrix[i][0 ]) { flag_col0 = true ; } for (int j = 1 ; j < n; j++) { if (!matrix[i][j]) { matrix[i][0 ] = matrix[0 ][j] = 0 ; } } } for (int i = m - 1 ; i >= 0 ; i--) { for (int j = 1 ; j < n; j++) { if (!matrix[i][0 ] || !matrix[0 ][j]) { matrix[i][j] = 0 ; } } if (flag_col0) { matrix[i][0 ] = 0 ; } } } };
注意 在尝试原地解决空间,最优化空间使用的时候,一定要确定好 用谁做标记 ,以及如何 避免标记被覆盖 ,还有 如何标记标记本身 ,需要慎重思考如何安排空间的利用,以及代码的顺序。
螺旋矩阵 算法概述 原题
题目要求为将矩阵按照顺时针方向扁平化得到一维数组。可以把矩阵看成一层一层从内向外的扩散,而每一层都是个大长方形,可以用相同的方法遍历,这样矩阵本身的其他属性就不重要了,也就意味着不需要额外做标记避免越界之类的问题,而是直接原地操作。
时间复杂度为O(mn):遍历矩阵
空间复杂度为O(1):原地操作
JAVA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution { public List<Integer> spiralOrder (int [][] matrix) { List<Integer> order = new ArrayList <Integer>(); if (matrix == null || matrix.length == 0 || matrix[0 ].length == 0 ) { return order; } int rows = matrix.length, columns = matrix[0 ].length; int left = 0 , right = columns - 1 , top = 0 , bottom = rows - 1 ; while (left <= right && top <= bottom) { for (int column = left; column <= right; column++) { order.add(matrix[top][column]); } for (int row = top + 1 ; row <= bottom; row++) { order.add(matrix[row][right]); } if (left < right && top < bottom) { for (int column = right - 1 ; column > left; column--) { order.add(matrix[bottom][column]); } for (int row = bottom; row > top; row--) { order.add(matrix[row][left]); } } left++; right--; top++; bottom--; } return order; } }
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution {public : vector<int > spiralOrder (vector<vector<int >>& matrix) { if (matrix.size () == 0 || matrix[0 ].size () == 0 ) { return {}; } int rows = matrix.size (), columns = matrix[0 ].size (); vector<int > order; int left = 0 , right = columns - 1 , top = 0 , bottom = rows - 1 ; while (left <= right && top <= bottom) { for (int column = left; column <= right; column++) { order.push_back (matrix[top][column]); } for (int row = top + 1 ; row <= bottom; row++) { order.push_back (matrix[row][right]); } if (left < right && top < bottom) { for (int column = right - 1 ; column > left; column--) { order.push_back (matrix[bottom][column]); } for (int row = bottom; row > top; row--) { order.push_back (matrix[row][left]); } } left++; right--; top++; bottom--; } return order; } };
注意 虽然说着简单但实现起来还是有难度的。主要难度在于要想到使用 四指针(上右下左)所框定的单位层同时向内层移动 ,以及如何遍历它们之间的范围且不引起冲突。while (left <= right && top <= bottom)
:最外层循环管理的是四指针逐渐走向矩阵中心的过程if (left < right && top < bottom)
:还需要注意的是,矩阵的结构 不一定是正方形 ,也就意味着,到最后的最后,可能只有上右或者下左可以遍历最中心那几个一维的元素,也就是说必须要多这一层if判断,否则就会重复遍历。 所以说要考虑的还是蛮多的,虽然解法思路简单,但是各种可能的情况还是都要考虑。
旋转图像 算法概述 原题
本题要求把矩阵顺时针旋转90度。可以通过计算得出每个元素在旋转90度之后的索引变化,用中间变量存储变化位置的元素值,然后循环旋转即可。
时间复杂度为O(n^2):遍历矩阵
空间复杂度为O(1):中间变量
JAVA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution { public void rotate (int [][] matrix) { int n = matrix.length; for (int i = 0 ; i < n / 2 ; ++i) { for (int j = 0 ; j < (n + 1 ) / 2 ; ++j) { int temp = matrix[i][j]; matrix[i][j] = matrix[n - j - 1 ][i]; matrix[n - j - 1 ][i] = matrix[n - i - 1 ][n - j - 1 ]; matrix[n - i - 1 ][n - j - 1 ] = matrix[j][n - i - 1 ]; matrix[j][n - i - 1 ] = temp; } } } }
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution {public : void rotate (vector<vector<int >>& matrix) { int n = matrix.size (); for (int i = 0 ; i < n / 2 ; ++i) { for (int j = 0 ; j < (n + 1 ) / 2 ; ++j) { tie (matrix[i][j], matrix[n - j - 1 ][i], matrix[n - i - 1 ][n - j - 1 ], matrix[j][n - i - 1 ]) \ = make_tuple (matrix[n - j - 1 ][i], matrix[n - i - 1 ][n - j - 1 ], matrix[j][n - i - 1 ], matrix[i][j]); } } } };
重要实例方法及属性(C++) std::tie(...variables)
:用于解包元组(类似于python的自动解包)std::make_tuple(...args)
:打包多个变量成元组
注意 公式本质是逆时针的,和题目要求的顺时针旋转是相反的。 要注意 代码本身逻辑可以与题目要求相反 ,同时达到相同效果。
搜素二维矩阵 II 算法概述 原题
题目要求为搜索矩阵中的某个目标值,且每行矩阵从左到右、从上到下升序排列。使用Z字形查找,从中间值开始,逐次比较移动。
时间复杂度为O(m+n):如果运气不好会多绕一圈
空间复杂度为O(1):行、列指针
JAVA 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public boolean searchMatrix (int [][] matrix, int target) { int m = matrix.length, n = matrix[0 ].length; int x = 0 , y = n - 1 ; while (x < m && y >= 0 ) { if (matrix[x][y] == target) { return true ; } if (matrix[x][y] > target) { --y; } else { ++x; } } return false ; } }
C++ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : bool searchMatrix (vector<vector<int >>& matrix, int target) { int m = matrix.size (), n = matrix[0 ].size (); int x = 0 , y = n - 1 ; while (x < m && y >= 0 ) { if (matrix[x][y] == target) { return true ; } if (matrix[x][y] > target) { --y; } else { ++x; } } return false ; } };
注意 我一开始想的是两层二叉查找,但实际上行内排序和列内排序本身是没有关联的,所以必须 一个一个移动查找 ,也就是给出的Z字形查找,因为 左边与上面的总是小的,右边和下面总是更大的 ,即使可能会稍微小绕一下,但也相对最简便了。要学会 从最基本的规律出发,从中间值开始 。