矩阵的顺时针打印

  • 前言
  • 一、矩阵顺时针打印
  • 1、题目
  • 2、解答
  • A.DFS
  • B.模拟
  • 总结
  • 参考文献


前言

通过矩阵的顺时针打印来学习改进的DFS、和模拟的思想。

一、矩阵顺时针打印

1、题目

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 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]

2、解答

A.DFS

按照自定义的顺序来DFS。
思想)开始向左递归,碰壁(越界或已访问)时,转向为向下;碰壁时,转向为向左;碰壁时,转向为向上。如此循环,直到四处碰壁,递归结束。

public class Imitate29 {
    //offer 29 顺时针打印矩阵。
    int count;//为res数组计数
    int[] res;//需返回的数组
    int[][] matrix;//传入矩阵
    int[][] visited;//是否被访问,0为未访问,1为访问过。

    public int[] spiralOrder(int[][] matrix) {
        //一、总体思路:dfs先左,后下,再上,再右,访问过的打标记来剪枝就行(建立在dfs上,但是做一些符合题意的递归走向)
        //1. 考虑空矩阵的情况
        if (matrix.length == 0 || matrix[0].length == 0)
            return new int[]{};
        //2. 初始化
        this.matrix = matrix;
        res = new int[matrix.length * matrix[0].length];
        visited = new int[matrix.length][matrix[0].length];
        //3. 调用dfs,给数组res填正确的值
        dfs(0, 0, 0);
        //4. 返回结果res
        return res;
    }

    public boolean dfs(int i, int j, int right) {
        //越界则返回true,表示走不通
        if (j == matrix[0].length || j == -1 || i == -1 || i == matrix.length)
            return true;
        //访问过就返回true,表示走不同
        if (visited[i][j] == 1)
            return true;
        //满足两个条件,开始记录数值,并设置是否访问过。
        res[count++] = matrix[i][j];
        visited[i][j] = 1;
        //right只的是一个方向,0:右;1:下;2:左;3:上。然后循环就可以把数据访问完。
        if (right == 0) {
            boolean flag = dfs(i, j + 1, right);
            //碰壁(越界或碰到以访问过的)时,才能开始转向。
            if (flag)
                dfs(i + 1, j, (right + 1) % 4);
        }
        //根据每次方向,来递归访问。
        if (right == 1) {
            boolean flag = dfs(i + 1, j, right);
            if (flag)
                dfs(i, j - 1, (right + 1) % 4);
        }
        if (right == 2) {
            boolean flag = dfs(i, j - 1, right);
            if (flag)
                dfs(i - 1, j, (right + 1) % 4);
        }
        if (right == 3) {
            boolean flag = dfs(i - 1, j, right);
            if (flag)
                dfs(i, j + 1, (right + 1) % 4);
        }
        return false;
    }
}

2)简化版

class Imitate {
    //offer 29 顺时针打印矩阵。
    int count;
    int[] res;
    int[][] matrix;
    int[][] visited;

    public int[] spiralOrder(int[][] matrix) {
        //dfs先左,后下,再上,再右,访问过的打标记来剪枝就行

        if (matrix.length == 0 || matrix[0].length == 0)
            return new int[]{};

        this.matrix = matrix;
        res = new int[matrix.length * matrix[0].length];
        visited = new int[matrix.length][matrix[0].length];

        dfs(0, 0, 0);

        return res;
    }

    public boolean dfs(int i, int j, int right) {
        if (j == matrix[0].length || j == -1 || i == -1 || i == matrix.length)
            return true;
        if (visited[i][j] == 1)
            return true;
        res[count++] = matrix[i][j];
        visited[i][j] = 1;
        if (right == 0 && dfs(i, j + 1, right)) {
            dfs(i + 1, j, (right + 1) % 4);
        } else if (right == 1 && dfs(i + 1, j, right)) {
            dfs(i, j - 1, (right + 1) % 4);
        } else if (right == 2 && dfs(i, j - 1, right)) {
            dfs(i - 1, j, (right + 1) % 4);
        } else if (right == 3 && dfs(i - 1, j, right)) {
            dfs(i, j + 1, (right + 1) % 4);
        }
        return false;
    }
}

B.模拟

模拟顺时针的过程。
思想)通过4个指针,指向行的有效开始和有效结束,指向列的有效开始和有效结束。然后如此顺时针循环填值,直到res数组被填满值。

public int[] spiralOrder(int[][] matrix) {
        //思路:模拟
        //1. 考虑空矩阵的情况
        if (matrix.length == 0 || matrix[0].length == 0)
            return new int[]{};
        //2. 初始化
        int[] res = new int[matrix.length * matrix[0].length];
        //3. 模拟
        //定义上下边界
        int i1, i2, j1, j2, count;
        //初始化各边界
        i1 = j1 = count = 0;
        i2 = matrix.length - 1;
        j2 = matrix[0].length - 1;
        //正式模拟
        while (count != res.length) {
            for (int i = j1; i <= j2 && count != res.length; i++) {
                res[count++] = matrix[i1][i];
            }
            i1++;
            for (int i = i1; i <= i2 && count != res.length; i++) {
                res[count++] = matrix[i][j2];
            }
            j2--;
            for (int i = j2; i >= j1 && count != res.length; i--) {
                res[count++] = matrix[i2][i];
            }
            i2--;
            for (int i = i2; i >= i1 && count != res.length; i--) {
                res[count++] = matrix[i][j1];
            }
            j1++;
        }
        //4. 返回结果res
        return res;
    }

总结

1)DFS的变形,掌握好DFS,在此基础上改进来完成自定义搜索的功能。
2)模拟,通过指针等附加手段来模拟实现。
3)Time:都是O(n),Space:都是O(n),但是模拟不需要递归栈,也不需要visited二维数组。