1.矩阵置零(难度等级:中)
重点是要考虑时间复杂度和空间复杂度。
直观上,我们首先需要遍历一次矩阵,并通过一个额外空间来记录哪些位置上出现了0。然后,我们需要再次遍历一次矩阵,通过这个额外空间来“传染”其他位置的元素。
如何优化这个额外空间的大小呢?
这里给出次优解(易理解和记忆):
class Solution {
public void setZeroes(int[][] matrix) {
boolean rowZero = false;
boolean colZero = false;
//判断第一列是否出现0元素
for(int i=0; i<matrix.length; i++){
if(matrix[i][0] == 0){
colZero = true;
}
}
//判断第一行是否出现0元素
for(int i=0; i<matrix[0].length; i++){
if(matrix[0][i] == 0){
rowZero = true;
}
}
//遍历矩阵,将0值记录到第一行或第一列中
for(int i=1; i<matrix.length; i++){
for(int j=1; j<matrix[0].length; j++){
if(matrix[i][j] == 0){
matrix[i][0] = matrix[0][j] = 0;
}
}
}
//其他元素传染传染
for(int i=1; i<matrix.length; i++){
for(int j=1; j<matrix[0].length; j++){
if(matrix[i][0] == 0 || matrix[0][j] == 0){
matrix[i][j] = 0;
}
}
}
//原来第一行或第一列的0元素传染
if(colZero){
for(int i=0; i<matrix.length; i++){
matrix[i][0] = 0;
}
}
if(rowZero){
for(int i=0; i<matrix[0].length; i++){
matrix[0][i] = 0;
}
}
}
}
2. 螺旋矩阵(难度等级:中)
这道题其实比较巧妙的思想是 将其看成“多层”的矩阵,然后你会发现:对于每一层来说,遍历的路线都是一致的耶!都是从左到右->从上到下->从右到左->从下到上。
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<>();
int row = matrix.length;
int col = matrix[0].length;
//分别表示矩阵每一“层”的边界(左,右,上,下)
int left = 0, right = col - 1, top = 0, bottom = row - 1;
while(left <= right && top <= bottom){
for(int i=left; i<=right; i++){
res.add(matrix[top][i]); //从左到右
}
for(int j=top+1; j<=bottom; j++){
res.add(matrix[j][right]); //从上到下
}
if(left<right && top<bottom){ //到最内层了就不用再执行后面的操作了
for(int i=right-1; i>=left; i--){
res.add(matrix[bottom][i]);
}
for(int j=bottom-1; j>top; j--){
res.add(matrix[j][left]);
}
}
left++;
right--;
top++;
bottom--;
}
return res;
}
}
3.旋转图像(难度等级:中)
本题的关键在于:不允许使用额外的矩阵,因此如何减小空间复杂度较为重要。
最为浅显的方式是 构造一个数组来缓存要操作的元素,以避免被覆盖,但是这种方式空间复杂度达到了。
这里提供一种较好理解和记忆的思路:顺时针旋转其实可以拆解为两次翻转操作——先水平翻转,再延着对角线翻转。
class Solution {
public void rotate(int[][] matrix) {
int m = matrix.length;
int n = matrix[0].length;
//水平翻转
for(int i=0; i<m/2; i++){
for(int j=0; j<n; j++){
int temp = matrix[i][j];
matrix[i][j] = matrix[m-i-1][j];
matrix[m-i-1][j] = temp;
}
}
//对角线翻转
for(int i=0; i<m; i++){
for(int j=0; j<i; j++){
int temp2 = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp2;
}
}
}
}
4.搜索二维矩阵(难度等级:中)
这一题其实难度不高,除了暴力解法外,首先能想到的就是对 行/列 的二分查找,但这种方式只能利用到行或者列其中一种的二分特性,并不是效率最高的。
其实仔细观察不难发现,我们可以构建一个以矩阵左下角为起点,变化的矩阵右上角为终点的“滑动矩阵”,通过查询结果结合矩阵的特性,来自适应地调整右上角的位置,直到最终查询到target。
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int x = 0;
int y = n-1;
while(y>=0 && x<m){
if(target == matrix[x][y]){
return true;
}
if(target > matrix[x][y]){
x++;
continue;
}
if(target < matrix[x][y]){
y--;
continue;
}
}
return false;
}
}
总结
其实从Hot 100中的矩阵题不难发现,关键点集中于考察空间复杂度。所以做题时仔细挖掘技巧,只要找到了技巧,再将技巧“翻译成”代码即可。