转载自: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下载