LeetCode100之螺旋矩阵(54)--Java

1.问题描述

        给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

        示例1

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

        示例2 

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

        提示

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 10
  • -100 <= matrix[i][j] <= 100

        难度等级

                中等

        题目链接

                螺旋矩阵

2.解题思路

        这道螺旋矩阵的题,说白了,就是按照顺时针绕圈进行遍历。我们可以将顺时针拆分为四个部分,从左到右、从上到下、从右到左和从下到上。

        知道题目该怎么做之后,我们可以定义四个变量,分别用来表示该次遍历的首尾行索引和首尾列索引(借助这四个索引实现绕圈),已经一个存储答案的List列表。

        //首行索引
       int startX = 0;
       //末尾行索引
       int endX = matrix.length-1;
       //首列索引
       int startY = 0;
       //末尾列索引
       int endY = matrix[0].length-1;
       //存储数据
       List<Integer> data = new ArrayList<>();

        因为我们每次遍历,其实是在绕圈,直到绕到最里面为止,也就是最终只剩一行或者最终只剩一列,前面提到的两种情况同时发生的话,则是在一个m*m的矩阵中,最后剩下中间那一个数。所以我们可以用一个循环来实现不断向矩阵内部绕圈,循环可以不断进行的条件就是首行索引不超过末尾行索引(取 = 时矩阵最终只剩一行)和首列索引不超过末尾列索引(取=时矩阵最终只剩一列)。

      while(startX <= endX && startY <= endY)
        ......

        接着我们就可以开始顺时针遍历了。

        根据题目要求,我们要先从左向右遍历,此时我们遍历的是未遍历过的那部分矩阵的首行,从首列一直遍历到末尾列。从左向右遍历结束后,我们要更新首行的索引,因为原来的首行已经被我们刚刚遍历过了。

        //从左到右
        for(int i = startY;i <= endY;i++){
            data.add(matrix[startX][i]);
        }
        //更新首行索引
        startX++;

        接着,我们要从上到下遍历,此时我们遍历的是未遍历过的那部分矩阵的末尾列,从首行一直遍历到末尾行。从上到下遍历结束后,我们也要更新末尾列的索引,因为原来的末尾列已经被我们刚刚遍历过了。

        //从上到下
        for(int i = startX;i <= endX;i++){
            data.add(matrix[i][endY]);
        }
        //更新末尾列索引
        endY--;

        然后,我们在这里还要做一个判断,因为首行与末尾列索引都改变了,我们此时要判断矩阵是否已经遍历完了,不是才要进行下面的遍历。

        //判断首行索引是否大于末尾行索引
        //判断首列索引是否大于末尾列索引
        if(startX > endX ||startY > endY){
            break;
        }

        如果矩阵还没遍历完,我们就要继续从右到左遍历,此时我们遍历的是未遍历过的那部分矩阵的末尾行,从末尾列一直遍历到首列。从右向左遍历结束后,我们要更新末尾列的索引,因为原来的末尾列已经被我们刚刚遍历过了。

        //从右到左
        for(int i = endY;i >= startY;i--){
            data.add(matrix[endX][i]);
        }
        //更新末尾行索引
        endX--;

        接着,我们要从下到上遍历,此时我们遍历的是未遍历过的那部分矩阵的首列,从末尾行一直遍历到首行。从下到上遍历结束之后,我们也要更新首列的索引,因为原来的首列已经被我们刚刚遍历过了。

        //从下到上
        for(int i = endX;i >= startX;i--){
            data.add(matrix[i][startY]);
        }
        //更新首列索引
        startY++;

        重复上述操作,直到整个矩阵被遍历完全。

        最后将存储答案的List集合返回即可。

     //返回结果
       return data;

3.代码展示

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        //首行索引
       int startX = 0;
       //末尾行索引
       int endX = matrix.length-1;
       //首列索引
       int startY = 0;
       //末尾列索引
       int endY = matrix[0].length-1;
       //存储数据
       List<Integer> data = new ArrayList<>();
       while(startX <= endX && startY <= endY){
        //从左到右
        for(int i = startY;i <= endY;i++){
            data.add(matrix[startX][i]);
        }
        //更新首行索引
        startX++;
        //从上到下
        for(int i = startX;i <= endX;i++){
            data.add(matrix[i][endY]);
        }
        //更新末尾列索引
        endY--;
        //判断首行索引是否大于末尾行索引
        //判断首列索引是否大于末尾列索引
        if(startX > endX ||startY > endY){
            break;
        }
        //从右到左
        for(int i = endY;i >= startY;i--){
            data.add(matrix[endX][i]);
        }
        //更新末尾行索引
        endX--;
        //从下到上
        for(int i = endX;i >= startX;i--){
            data.add(matrix[i][startY]);
        }
        //更新首列索引
        startY++;
       }
     //返回结果
       return data;
    }
}

4.总结

        这道题大体的思路不难,就是顺时针遍历+绕圈而已。我们可以将顺时针遍历分为从左到右、从上到下、从右到左已经从下到上四个部分,用循环来实现绕圈。好了,这道题就简单啰嗦到这里,祝大家刷题愉快~

<think>我们正在处理的是LeetCode54题:螺旋矩阵。给定一个mxn的矩阵,按照螺旋顺序返回矩阵中的所有元素。根据引用内容,我们可以采用模拟螺旋路径的方法,使用方向数组来控制移动方向,当遇到边界或已访问的元素时改变方向。算法步骤:1.定义方向数组DIRS,表示右、下、左、上四个方向(顺时针)。2.初始化一个与输入矩阵同样大小的visited矩阵(或者修改原矩阵标记已访问)?但引用[1]中使用了将访问过的位置标记为Integer.MAX_VALUE的方法,这样可以在原矩阵上操作,避免额外空间。但注意题目要求不能修改原矩阵?题目没有明确,但通常我们应避免修改原数据。不过引用[1]的方法修改了原矩阵,如果要求不修改,则需要额外空间。3.另一种方法是使用边界收缩法(引用[3]提到),但引用[1]的方法更简洁。然而,题目要求返回列表,不要求保留原矩阵,所以我们可以修改原矩阵(但题目没有说可以修改,所以最好不修改?)。但引用[1]的解法修改了原矩阵。我们也可以使用一个visited数组来记录访问过的位置,这样就不修改原矩阵。考虑到题目矩阵大小不超过10x10,使用额外空间是可以接受的。但引用[1]的方法已经比较高效,且代码简洁。这里我们采用引用[1]的方法,但注意题目中矩阵元素可能包含负数,所以标记值不能是Integer.MIN_VALUE?因为题目中元素范围在-100100,所以我们可以使用一个超出这个范围的值(比如101或Integer.MAX_VALUE)作为标记。但是,题目没有说不能修改原矩阵,所以我们可以使用。但是,如果我们不想修改原矩阵,我们可以使用一个同样大小的boolean数组来记录访问情况。两种方法都可以,这里我们提供两种实现,但根据引用[1],我们先给出修改原矩阵的实现(因为代码简洁),然后再提供一个不修改原矩阵的实现(使用边界收缩法)。不过,引用[3]提到边界收缩法,我们也可以实现。由于问题要求包含输入输出示例,我们将提供:1.算法实现(两种方法)2.输入输出示例方法一:模拟路径(修改原矩阵-使用方向数组,按顺序向右、向下、向左、向上移动。-当下一步到达矩阵边界或已经访问过的位置时,改变方向(通过取余循环切换方向)。-使用一个变量记录当前方向,每次移动后判断下一步是否需要转向。方法二:边界收缩法(不修改原矩阵-定义四个边界:上边界top=0,下边界bottom=m-1,左边界left=0,右边界right=n-1。-按照顺时针方向遍历:1.从左到右遍历上边界(top行),遍历完上边界下移(top++)2.从上到下遍历右边界(right列),遍历完右边界左移(right--)3.从右到左遍历下边界(bottom行),遍历完下边界上移(bottom--)4.从下到上遍历左边界(left列),遍历完左边界右移(left++)-循环条件:top<=bottom且left<=right方法二更直观,且不需要修改原矩阵,也不需要额外的标记数组,空间复杂度O(1)(除了输出列表)。我们选择方法二作为主要实现,因为它更符合题目要求(不修改原矩阵)且高效。下面给出Java实现:方法二:边界收缩法步骤:初始化top=0,bottom=m-1,left=0,right=n-1。初始化结果列表ans。while(top<=bottom&&left<=right){1.从左到右:遍历top行的left到right列,将元素加入ans。然后top++。2.从上到下:遍历right列的top到bottom行,将元素加入ans。然后right--。3.检查是否还有行(因为top可能已经超过bottom):从右到左:遍历bottom行的right到left列(逆序),将元素加入ans。然后bottom--。4.检查是否还有列:从下到上:遍历left列的bottom到top行(逆序),将元素加入ans。然后left++。}注意:在第3步和第4步之前,需要检查是否还有行/列,因为可能已经遍历完了(比如只有一行或一列的情况)。具体代码:输入输出示例:输入:matrix=[[1,2,3,4],[5,6,7,8],[9,10,11,12]]输出:[1,2,3,4,8,12,11,10,9,5,6,7]代码实现:</think>### LeetCode 54螺旋矩阵 Java 实现 #### 问题描述 给定一个 m×n 的矩阵,按照顺时针螺旋顺序返回矩阵中的所有元素。 **示例输入**: ```java int[][] matrix = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; ``` **示例输出**: ``` [1,2,3,4,8,12,11,10,9,5,6,7] ``` --- ### 高效实现方案(边界收缩法) ```java import java.util.ArrayList; import java.util.List; class Solution { public List<Integer> spiralOrder(int[][] matrix) { List<Integer> result = new ArrayList<>(); if (matrix == null || matrix.length == 0) return result; int top = 0, bottom = matrix.length - 1; int left = 0, right = matrix[0].length - 1; while (top <= bottom && left <= right) { // 1. 从左到右遍历顶层 for (int j = left; j <= right; j++) { result.add(matrix[top][j]); } top++; // 2. 从上到下遍历右侧 for (int i = top; i <= bottom; i++) { result.add(matrix[i][right]); } right--; // 3. 检查是否仍有行需要处理 if (top <= bottom) { // 从右到左遍历底层 for (int j = right; j >= left; j--) { result.add(matrix[bottom][j]); } bottom--; } // 4. 检查是否仍有列需要处理 if (left <= right) { // 从下到上遍历左侧 for (int i = bottom; i >= top; i--) { result.add(matrix[i][left]); } left++; } } return result; } } ``` --- ### 算法解析 1. **边界初始化**: - `top=0`, `bottom=行数-1` - `left=0`, `right=列数-1` 2. **四步循环**: ```java while (top <= bottom && left <= right) { // 向右遍历 → [top行, left→right] // 向下遍历 ↓ [right列, top→bottom] // 向左遍历 ← [bottom行, right→left] (需检查top<=bottom) // 向上遍历 ↑ [left列, bottom→top] (需检查left<=right) } ``` 3. **边界收缩**: - 每完成一个方向遍历后收缩对应边界 - 例如:完成顶层遍历后 `top++` 4. **条件检查**: - 向左/向上遍历前需检查边界有效性 - 防止单行/单列情况下的重复遍历[^1] --- ### 时间复杂度分析 - **时间复杂度**:$O(m \times n)$ 需访问矩阵中的每个元素恰好一次 - **空间复杂度**:$O(1)$(不考虑输出列表) 仅使用固定数量的指针变量 --- ### 测试用例 ```java public static void main(String[] args) { Solution solution = new Solution(); // 示例测试 int[][] matrix1 = {{1,2,3},{4,5,6},{7,8,9}}; System.out.println(solution.spiralOrder(matrix1)); // 输出: [1,2,3,6,9,8,7,4,5] int[][] matrix2 = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; System.out.println(solution.spiralOrder(matrix2)); // 输出: [1,2,3,4,8,12,11,10,9,5,6,7] // 边界测试 int[][] matrix3 = {{7}}; System.out.println(solution.spiralOrder(matrix3)); // 输出: [7] } ``` --- ### 应用场景 1. **图像处理**:像素矩阵螺旋遍历 2. **游戏开发**:地图探索路径规划 3. **数据压缩**:矩阵数据的螺旋编码 4. **内存管理**:二维存储结构的访问优化[^3] --- ### 相关问题 1. 如何实现**逆时针**螺旋遍历矩阵? 2. 如何处理**非矩形**(如三角形)矩阵螺旋遍历? 3. 如何将螺旋遍历算法扩展**三维矩阵**? 4. 在图像处理中,螺旋遍历如何优化**局部性原理**? 5. 如何修改算法实现**从外向内**和**从内向外**两种螺旋顺序?[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiao--xin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值