java算法之简单的矩阵螺旋式遍历Spiral Matrix

转载自:http://blog.csdn.net/ylyg050518/article/details/48547619

继续看一个与数组操作相关的算法,这道题目给我们提供了一个遍历二维数组的新方式——螺旋式遍历。


问题描述

原文:

Given a matrix of m × n elements ( m rows, n columns), return all elements of the matrix in spiral order. 
For example, Given the following matrix: 

 [ 1, 2, 3 ], 
 [ 4, 5, 6 ], 
 [ 7, 8, 9 ] 

You should return [1,2,3,6,9,8,7,4,5]

大意:给一个m x n(m行,n列)的矩阵,返回矩阵所有元素的螺旋访问序列。

思路分析

  题目给我们提供了一个的新的角度来遍历二维数组,通常情况下我们会以行优先或者列优先的方式进行数组的遍历。螺旋式遍历的方式从数组的外层开始,层层深入,直到所有元素被访问到。要想实现这种遍历方式,最直观和容易想到的就是模拟法,模拟你手动写出遍历序列的过程。而关键点在于如何实现这种访问方式,显然我们仍要依赖循环的方式,仔细分析可以发现,遍历的过程可以分为四个步骤,从矩阵左上到右上,从右上到右下,从右下到左下,从左下到左上,然后依次向内层递进,循环往复。程序中我们可以设置4个循环来依次描述这4个方向上的遍历过程,现在的关键问题就是如何控制循环中游标的起始位置和终止位置。可以明确指导,当一个方向上的遍历完成后,下次再沿同一方向遍历时,起始元素和终止元素都要都要向中心收缩一位,我们可以设置4个变量beginX,endX,beginY,endY,分别描述X(水平方向)Y(垂直方向)游标的范围变化特征。以下,我们给出实际的代码。

/*
* 螺旋方式访问二维数组,返回结果集合
*/

public static List spiralOrder(int[][] matrix) {
    List result = new ArrayList();
    int m = matrix.length;
    int n = matrix[0].length;
    int beginX, endX, beginY, endY;
    beginX = 0;
    endX = n - 1;
    beginY = 0;
    endY = m - 1;
    while (true) {
        // 从左上到右上
        for (int j = beginX; j <= endX; j++) {
            result.add(matrix[beginY][j]);
        }
        beginY++;
        if (beginY > endY)
            break;
        // 从右上到右下
        for (int i = beginY; i <= endY; i++) {
            result.add(matrix[i][endX]);
        }
        endX--;
        if (endX < beginX)
            break;

        // 从右下到左下
        for (int j = endX; j >= beginX; j--) {
            result.add(matrix[endY][j]);
        }
        endY--;
        if (endY < beginY)
            break;

        // 从左下到左上
        for (int i = endY; i >= beginY; i--) {
            result.add(matrix[i][beginX]);
        }
        beginX++;
        if (beginX > endX)
            break;
    }
    return result;

}


说明:以上算法时间复杂度为O(n²).注意循环的终止条件,当任意方向上的游标出现本末倒置的情况,即起始游标值大于终结游标的条件下,说明遍历已完成,程序应该跳出循环,返回最终遍历的集合

问题变形

原文

Given an integer n, generate a square matrix filled with elements from 1 to n² in spiral order. 
For example, Given n = 3, 
You should return the following matrix: 

 [ 1, 2, 3 ], 
 [ 8, 9, 4 ], 
 [ 7, 6, 5 ] 
]

大意:给一个整型数n,生成一个n * n方阵,并按照螺旋的遍历方式,从1到n²填充这个方阵。

分析

  这道题目是上一个题目的变形,有了上一个题目的基础,我们只需修改for循环内部的操作就行了,给出以下代码。

public static int[][] generateMatrix2(int n) {
        int[][] result = new int[n][n];
        int num, beginX, endX, beginY, endY;
        num = 1;
        beginX = 0;
        endX = n - 1;
        beginY = 0;
        endY = n - 1;
        while (true) {
            // 从左上到右上
            for (int j = beginX; j <= endX; j++) {
                result[beginX][j] = num++;
            }
            beginY++;
            if (beginY > endY)
                break;
            // 从右上到右下
            for (int i = beginY; i <= endY; i++) {
                result[i][endX] = num++;
            }
            endX--;
            if (endX < beginX)
                break;

            // 从右下到左下
            for (int j = endX; j >= beginX; j--) {
                result[endY][j] = num++;
            }
            endY--;
            if (endY < beginY)
                break;

            // 从左下到左上
            for (int i = endY; i >= beginY; i--) {
                result[i][beginX] = num++;
            }
            beginX++;
            if (beginX > endX)
                break;
        }
        return result;
    }


说明:算法时间复杂度仍然为O(n²)。但是有一点小问题,可以知道,变形后的问题中m * n常规矩阵变成了n * n的方阵,但是我们仍然利用了4个变量,可其中有两个变量其实重复的,我们能不能稍作改进呢?我们直接给出只用两个游标变量的遍历方法。

/*
     * 按照螺旋式生成数组,返回生成后的二维数组
     */
public static int[][] generateMatrix1(int n) {
    int[][] result = new int[n][n];
    if (n <= 0)
        return null;
    int begin = 0, end = n - 1;
    int num = 1;
    while (begin < end) {
        // 从左到右
        for (int j = begin; j < end; j++) {
            result[begin][j] = num++;
            System.out.print("[" + begin + "," + j + "] ");
        }
        System.out.println();
        // 从上到下
        for (int i = begin; i < end; i++) {
            result[i][end] = num++;
            System.out.print("[" + i + "," + end + "] ");
        }
        System.out.println();
        // 从右到左
        for (int j = end; j > begin; j--) {
            result[end][j] = num++;
            System.out.print("[" + end + "," + j + "] ");
        }
        System.out.println();
        // 从下到上
        for (int i = end; i > begin; i--) {
            result[i][begin] = num++;
            System.out.print("[" + i + "," + begin + "] ");
        }
        System.out.println();
        begin++;
        end--;

    }
    if (begin == end) {
        result[begin][end] = num;
        System.out.print("[" + begin + "," + end + "] ");
    }
    return result;
}


说明:此种写法类似和之前的方法类似,无非是将游标变量的增减操作操作放在了循环的末尾统一进行,改进了while(条件)中的条件,并将最后一个元素的遍历放在循环之外。

这两种遍历方法的本质差别,在于遍历的数目划分不同罢了。如:int[] a={1,2,3,4,5,6,7,8,9} 该方法第一次遍历1,2;上面的方法遍历1,2,3;

Demo下载

你可能感兴趣的:(java,算法)