1. 程式人生 > >對角線列印二維陣列問題

對角線列印二維陣列問題

1 前言

最近在網上看到這樣一道面試題:二維陣列(N*N),沿對角線方向,從右上角列印到左下角如N=4:
4*4二維陣列
{ 1 2 3 4 }
{ 5 6 7 8 }
{ 9 10 11 12 }
{13 14 15 16 }

列印順序
4
3 8
2 7 12
1 6 11 16
5 10 15
9 14
13

網上已經有各種解法,也有現成的程式,但是個人都不是很滿意,網路上的思路都不是很清晰。現在提供一種分析思路及原始碼

2 分析思路

對於一個二維陣列,我們畫出示意圖如下:
這裡寫圖片描述
當column = row時,就是一個方正的陣列,我們現在來考慮如何列印符合題目要求的。
其實這個問題優一種很簡單的解法,就是在數很小時,寫出每個列印的座標,然後來找規律。但是我覺得不夠好,我們先來畫出題目要求打出的順序的示意圖。(4 X 4 陣列)
這裡寫圖片描述


根據示意圖,我們可以分兩步來考慮這個問題。
第一步:先列印那四根黑色的線串聯起來的元素。如果我們按照逆時針的方向來串聯元素,那麼就有如下的示意圖:
這裡寫圖片描述
0輪列印 3
1輪列印 2 7
2輪列印 1 6 11
。。。

假設陣列有n * n 大小,k從 n-1 迴圈到 0
(n-1 - k) 輪列印為 a[0][k] a[1][k + 1] ….a[n-1 - k][n-1] ,那麼對於每一輪列印,我們就有以下程式碼
我們可以認為,第一步就是從n - 1列遍歷到0列

        //右上角,我們可以認為先從n - 1 到 0 列
        for (int k = n - 1
; k >= 0; k--){ //每一列的迴圈 行下標i會增加,列下標j會增加 for (i = 0 ,j = k; i <= n - 1 - k && j <= n - 1 ; i++,j++){ LogUtil.print(getFixedLenString(array[i][j] + "",len," ") + " "); } //換行 LogUtil.println(""); }

第二步:處理到了0這個對角線之後,我們可以看藍色的三根線,這裡我們可以認為從1 行遍歷到n -1行,那麼k從1 迴圈到n-1
k輪:a[k][0] a[k+1][1] … a[n-1][n-1 - k]

        //再從1 到 n - 1 行
        for (int k = 1 ; k <= n - 1; k++){
            //每一列的迴圈 行下標i會增加,列下標j會增加
            for (i = k ,j = 0; i <= n - 1 && j <= n - 1 - k ; i++,j++){
                LogUtil.print(getFixedLenString(array[i][j] + "",len," ") + " ");
            }
            //換行
            LogUtil.println("");
        }

這樣將兩個迴圈合併在一起,我們就認為,我們把這個陣列按照從右上角對角線列印全部列印完了。

    /**
     * 左上角開始列印二維矩陣陣列
     * @param array
     */
    private static void printTwoDimensionalArrayTopRight(int[][] array){
        int n = array.length;
        int maxSize = n * n;
        int len = (maxSize + "").length();

        //初始角標
        int i = 0;
        int j = 0;

        //右上角,我們可以認為先從n - 1 到 0 列
        for (int k = n - 1 ; k >= 0; k--){
            //每一列的迴圈 行下標i會增加,列下標j會增加
            for (i = 0 ,j = k; i <= n - 1 - k && j <= n - 1 ; i++,j++){
                LogUtil.print(getFixedLenString(array[i][j] + "",len," ") + " ");
            }
            //換行
            LogUtil.println("");
        }

        //再從1 到 n - 1 行
        for (int k = 1 ; k <= n - 1; k++){
            //每一列的迴圈 行下標i會增加,列下標j會增加
            for (i = k ,j = 0; i <= n - 1 && j <= n - 1 - k ; i++,j++){
                LogUtil.print(getFixedLenString(array[i][j] + "",len," ") + " ");
            }
            //換行
            LogUtil.println("");
        }
        LogUtil.println("");
    }

執行開始的題目,我們能得到的輸出結果如下:

1  2  3  4  
5  6  7  8  
9  10 11 12 
13 14 15 16 

4  
3  8  
2  7  12 
1  6  11 16 
5  10 15 
9  14 
13 

3 拓展

我們再來看看之前的分析思路:
這裡寫圖片描述
1 首先看箭頭:方向為右下,根據陣列座標系原則,我們可以知道a[i][j]在每一輪內迴圈中都會自增.
只需要知道初始a[i][j]與結束a[i][j]時就可以開始迴圈了
2 對於右上角開始的對角線列印,我們按照逆時針的方向來看,先從n - 1 到 0 列,再從1 行到n行。
n - 1 到 0 列 起點座標為:a[0][k] 終點座標:a[n - 1 - k][n-1] i++,j++ k從n-1迴圈到0
1 到 n -1 行 起點座標為:a[k][0] 終點座標:a[n - 1][n-1 - k] i++,j++ k從1迴圈到n-1

類似的,如果我們是從左上角開始列印呢?那麼有如下的示意圖:
這裡寫圖片描述
1 根據箭頭方向及陣列座標系,i++,j–
2 根據順時針來看,先是0到n-1列,再是1到n-1行
0到n-1列 起點座標a[0][k] 終點座標a[k][0] k從0迴圈到n-1
1 到n-1行 起點座標a[k][n-1] 終點座標a[n-1][k] k從1迴圈到n-1
那麼就有以下程式碼

    /**
     * 左上角開始列印二維矩陣陣列,按照順時針方向來選取
     * @param array
     */
    private static void printTwoDimensionalArrayTopLeft(int[][] array){
        int n = array.length;
        int maxSize = n * n;
        int len = (maxSize + "").length();

        //初始角標
        int i = 0;
        int j = 0;

        //左上角,我們可以認為先從0 到 n - 1 列
        for (int k = 0 ; k <= n - 1; k++){
            //每一列的迴圈 行下標i會增加,列下標j會減少
            for (i = 0 ,j = k; i <= k && j >= 0 ; i++,j--){
                LogUtil.print(getFixedLenString(array[i][j] + "",len," ") + " ");
            }
            //換行
            LogUtil.println("");
        }

        //再從1 到 n - 1 行
        for (int k = 1 ; k <= n - 1; k++){
            //每一列的迴圈 行下標i會增加,列下標j會減少
            for (i = k ,j = n - 1; i <= n - 1 && j >= k ; i++,j--){
                LogUtil.print(getFixedLenString(array[i][j] + "",len," ") + " ");
            }
            //換行
            LogUtil.println("");
        }
        LogUtil.println("");
    }

輸出結果:

1  2  3  4  
5  6  7  8  
9  10 11 12 
13 14 15 16 

1  
2  5  
3  6  9  
4  7  10 13 
8  11 14 
12 15 
16 

特別的對於左下角,右下角,我們都有對應的方法分析:
左下角:
這裡寫圖片描述
1 根據箭頭方向及陣列座標系,i++,j++
2 根據順時針來看,先是n-1行到0行,再是1到n-1列
n-1行到0行 起點座標a[k][0] 終點座標a[n-1][n-1-k] k從n-1迴圈到0
1 到n-1列 起點座標a[0][k] 終點座標a[n-1-k][n-1] k從1迴圈到n-1
程式碼就不貼了

右下角:
這裡寫圖片描述
1 根據箭頭方向及陣列座標系,i++,j–
2 根據逆時針來看,先是n-1到0行,再是n-2到0列
n-1到0行 起點座標a[k][n-1] 終點座標a[n-1][k] k從0迴圈到n-1
n-2到0列 起點座標a[0][k] 終點座標a[k][0] k從n-2迴圈到0
程式碼就不貼了

最後:參考程式碼https://github.com/qiyei2015/Algorithms/tree/master/src/com/qiyei/array