算法系列--Spiral Matrix

来源:互联网 发布:大庆油田网络客服电话 编辑:程序博客网 时间:2024/06/05 04:14

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


问题描述

原文:

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<Integer> spiralOrder(int[][] matrix) {    List<Integer> result = new ArrayList<Integer>();    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(条件)中的条件,并将最后一个元素的遍历放在循环之外。并没有提高程序效率,所以读者只需要掌握一种方法即可。

0 0
原创粉丝点击