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,
/* * 螺旋方式訪問二維陣列,返回結果集合 */ 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(條件)中的條件,並將最後一個元素的遍歷放在迴圈之外。
這兩種遍歷方法的本質差別,在於遍歷的數目劃分不同罷了。如:int[] a={1,2,3,4,5,6,7,8,9} 該方法第一次遍歷1,2;上面的方法遍歷1,2,3;