1. 程式人生 > >java演算法之簡單的矩陣螺旋式遍歷Spiral Matrix

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<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;