對角線列印二維陣列問題
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