LeetCode热题100(Java)(6)矩阵

本章包括的题目有:

73. 矩阵置零 - 力扣(LeetCode)

54. 螺旋矩阵 - 力扣(LeetCode)

48. 旋转图像 - 力扣(LeetCode)

240. 搜索二维矩阵 II - 力扣(LeetCode)

1.矩阵置零

思路解析:

有一种简单的思路是创建一个矩阵,标记每个0元素的位置,然后在原矩阵上根据标出来的位置将其行和列置为0。显然,这种思路的空间复杂度是 O(m n) 。当然,我们可以对其进行优化,创建两个数组,在遍历原矩阵的过程中,分别将遍历到的0位置的横纵坐标分别存入对应数组中,由于可能会有相同的横纵坐标,所以我们这里用HashSet去重,可以适当的优化时间效率。

代码实现:

java 复制代码
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

class Solution {
    public void setZeroes(int[][] matrix) {
        Set<Integer> line = new HashSet<>();
        Set<Integer> row = new HashSet<>();
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                if(matrix[i][j] == 0){
                    line.add(i);
                    row.add(j);
                }
            }
        }
        for(Integer l : line){
            for (int i = 0; i < matrix[0].length; i++) {
                matrix[l][i] = 0;
            }
        }

        for(Integer r : row){
            for (int i = 0; i < matrix.length; i++) {
                matrix[i][r] = 0;
            }
        }

    }
}

时间复杂度O(m*n)

空间复杂度O(m + n)

当然,我们也可以原地对其进行标记,得到空间复杂度为 O(1)的方法,可以先定义两个变量,看一下第一行和第一列是否需要被标记为0。然后遍历数组,若某个位置是0,则将其对应的行的第一个和列的第1个位置都标记为0,这样遍历完成之后,再通过第一行和第一列被标记为0的数据去调整整个数组。

如图所示,第一张为原数组,由于第一行有0,所以我们要用一个常量标记第一行,最终要全部置为0,然后将中间位置的0分别将其对应的行列的第一个位置标为0,这样在遍历行的时候,把行对应的所有列标记为0,遍历列的时候,把列对应的所有行标记为0,最后再看常量是否标记第一行和第一列,即可完成整个矩阵的置零操作。

2.螺旋矩阵

思路解析:

螺旋矩阵思路简单,但是代码的细节问题较多,笔者写的时候的思路是,先创建一个矩阵去表示当前位置的值是否被访问过。然后依次向左、向下、向右、向上循环,直到访问到边界,或者访问到被访问过的元素,依次访问完所有元素,即可得到答案。

代码实现:

java 复制代码
class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;
        ArrayList<Integer> ret = new ArrayList<>();
        boolean[][] visited = new boolean[m][n];
        int i = 0,j = 0;
        while(m * n != ret.size()){
            while(j >= 0 && j < n && i >= 0 && i < m && !visited[i][j]){
                ret.add(matrix[i][j]);
                visited[i][j] = true;
                j++;
                if(j >= n || visited[i][j]) {
                    j--;
                    i++;
                    break;
                }
            }
            while(j >= 0 && j < n && i >= 0 && i < m && !visited[i][j]){
                ret.add(matrix[i][j]);
                visited[i][j] = true;
                i++;
                if(i >= m || visited[i][j]) {
                    i--;
                    j--;
                    break;
                }
            }
            while(j >= 0 && j < n && i >= 0 && i < m && !visited[i][j]){
                ret.add(matrix[i][j]);
                visited[i][j] = true;
                j--;
                if(j < 0 || visited[i][j]) {
                    j++;
                    i--;
                    break;
                }
            }
            while(j >= 0 && j < n && i >= 0 && i < m && !visited[i][j]){
                ret.add(matrix[i][j]);
                visited[i][j] = true;
                i--;
                if(i < 0 || visited[i][j]) {
                    i ++;
                    j ++;
                    break;
                }
            }
        }
        return ret;
    }
}

但是这样写极容易出错,我们可以通过维护四个边界的方式,一步步缩小整个矩阵的搜索范围。维护四个边界:top, bottom, left, right,按顺序打印: 上行:从 left 到 right,然后 top++ ;右列:从 top 到 bottom,然后 right-- ;下行:从 right 到 left,然后 bottom-- ;左列:从 bottom 到 top,然后 left++

代码实现:

java 复制代码
class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> res = new ArrayList<>();
        if (matrix.length == 0) return res;
        
        int top = 0, bottom = matrix.length - 1;
        int left = 0, right = matrix[0].length - 1;
        
        while (top <= bottom && left <= right) {
            for (int j = left; j <= right; j++) {
                res.add(matrix[top][j]);
            }
            top++;
            
            for (int i = top; i <= bottom; i++) {
                res.add(matrix[i][right]);
            }
            right--;
            
            if (top <= bottom) {
                for (int j = right; j >= left; j--) {
                    res.add(matrix[bottom][j]);
                }
                bottom--;
            }
            
            if (left <= right) {
                for (int i = bottom; i >= top; i--) {
                    res.add(matrix[i][left]);
                }
                left++;
            }
        }
        return res;
    }
}

3.旋转图像

思路解析:

我们可以将矩阵层层转置,通过四角轮换的方式

代码实现:

java 复制代码
class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        for (int i = 0; i < n / 2; i++) {
            for (int j = i; j < n - 1 - i; j++) {
                swap(i ,j, matrix);
            }
        }
    }

    private void swap(int i, int j, int[][] matrix) {
        //[i][j], [j][len - 1 - i], [len - 1 - i][len - 1 - j], [len - 1 - j][i]
        int n = matrix.length;
        int temp = matrix[i][j];
        matrix[i][j] = matrix[n - 1 - j][i];
        matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j];
        matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i];
        matrix[j][n - 1 - i] = temp;
    }
}

时间复杂度O(n*n)

空间复杂度O(1)

拓展思路:

也可以通过转置加水平翻转的方式得到结果。先沿对角线将矩阵转置,然后将矩阵水平翻转,即可得到顺时针旋转的结果。

代码实现:

java 复制代码
public void rotate(int[][] matrix) {
    int n = matrix.length;

    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[j][i];
            matrix[j][i] = temp;
        }
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n / 2; j++) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[i][n - 1 - j];
            matrix[i][n - 1 - j] = temp;
        }
    }
}

时间复杂度O(n*n)

空间复杂度O(1)

4.搜索二维矩阵

思路解析:

此题的关键在于我们要从右上角或者从左下角开始,这样的话,我们每次进行选择,就能排除掉一行或者一列。我们拿右上角举例,右上角如果往左走就是变小,往下走就是变大,可以通过对比当前值与目标值的大小,选择往下走或是往左走(类似二叉排序数)。

代码实现:

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) 
            return false;
        
        int m = matrix.length;
        int n = matrix[0].length;
        int i = 0, j = n - 1; // 从右上角开始
        
        while (i < m && j >= 0) {
            if (matrix[i][j] == target) {
                return true;
            } else if (matrix[i][j] > target) {
                j--; // 当前值太大,排除当前列
            } else {
                i++; // 当前值太小,排除当前行
            }
        }
        return false;
    }
}

时间复杂度O(m + n)

空间复杂度O(1)

上一章:

LeetCode热题100(Java)(5)普通数组-CSDN博客

下一章:

链表(上)

相关推荐
代码AI弗森7 小时前
一文理清楚“算力申请 / 成本测算 / 并发评估”
java·服务器·数据库
Old Uncle Tom7 小时前
OpenClaw 记忆系统 -- 记忆预加载
java·数据结构·算法·agent
小小小米粒7 小时前
Collection单列集合、Map(Key - Value)双列集合,多继承实现。
java·开发语言·windows
摇滚侠8 小时前
expdp 查看帮助
java·数据库·oracle
:1218 小时前
java基础
java·开发语言
圣保罗的大教堂9 小时前
leetcode 1855. 下标对中的最大距离 中等
leetcode
曹牧9 小时前
Spring:@RequestMapping注解,匹配的顺序与上下文无关
java·后端·spring
daixin88489 小时前
cursor无法正常使用gpt5.5等模型解决方案
java·redis·cursor
韦禾水10 小时前
记录一次项目部署到tomcat的异常
java·tomcat