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博客

下一章:

链表(上)

相关推荐
Dicky-_-zhang1 天前
消息队列Kafka/RocketMQ选型与高可用架构:从单体到100万TPS的演进
java·jvm
晨曦中的暮雨1 天前
4.15腾讯 CSIG云服务产线 一面
java·开发语言
fake_ss1981 天前
AI时代学习全栈项目开发的新范式
java·人工智能·学习·架构·个人开发·学习方法
茉莉玫瑰花茶1 天前
工作流的常见模式 [ 1 ]
java·服务器·前端
未若君雅裁1 天前
Spring AOP、日志切面与声明式事务原理
java·后端·spring
No8g攻城狮1 天前
【人大金仓】wsl2+ubuntu22.04安装人大金仓数据库V9
java·数据库·spring boot·非关系型数据库
xiaoerbuyu12331 天前
开源Java 邮箱 基于SpringBoot+Vue前后端分离的电子邮件
java·开发语言
C+++Python1 天前
C++ 进阶学习完整指南
java·c++·学习
zhangjw341 天前
第11篇:Java Map集合详解,HashMap底层原理、哈希冲突、JDK1.8优化、遍历方式彻底吃透
java·开发语言·哈希算法