zigzag數組:輸入n,求一個nXn矩陣,規定矩陣沿45度遞增,形成一個zigzag數組

轉載自:https://blog.csdn.net/u013074465/article/details/43062985

同樣是找規律的螺旋隊列見這個帖子

面試題目:

輸入n,求一個nXn矩陣,規定矩陣沿45度遞增,形成一個zigzag數組(JPEG編碼裏取像素數據的排列順序),請問如何用C++實現?

(中國臺灣著名硬件公司2007年11月面試題)(自程序員面試寶典第四版92頁)


看了網上的好幾篇文章,由於本人 愚鈍,未能理解,故自己認真想了想,總結如下。

對zigzag數組,可以理解爲一個矩陣,分爲兩部分來解決:右上部分和左下部分

數組下標

圖1 給出了一個n=7時候的矩陣的下標變化情況,例如“24”表示矩陣的第2行第4列(從0開始)。

  

右上部分

右上部分數字依次增大的順序如圖2的紅色箭頭所示,該部分包括矩陣的對角線。

注意下面的分析中,最終的規律都要轉化到下標上,因爲該二維數組是和下標緊密相關的

對右上部分來說:結合圖1和圖2可以看出幾個規律:

(1)沿着紅色箭頭的方向,數組的值從0開始,每次加1,逐漸增大。

(2)每條紅線上的下標之和均相等,例如第三條紅線,下標和都是2;並且下標和從0開始,每次加1,依次增加。矩陣對角線的下標和是6。

(3)第零條紅線上數字個數爲一個(下標和爲0),第一條紅線上數字爲2個(下標和爲1),……,對角線爲第六條紅線,數字個數爲七(下標和爲6)。即:第s條紅線上數字個數爲s + 1個,s = 下標和。設二維數組的某個下標爲(i,j),那麼下標和s = i + j

(4)每條斜線上第一個數字(也是最小的數字)是上一行最大數字加1。也等於之前所有斜線上的數字的個數之和,例如第四條斜線上第一個數組是6,它等於第一、二、三條斜線上的數字之和6。

(5)而每條斜線上數字個數是一個等差數列,第0斜線1個,第1斜線2個,……,第s斜線s + 1個。又因爲 s = i + j ,  因此,前s條(從0條到第s-1條)斜線上一共有1 + 2 + ……+ s個數字,也就是s(s+1)/2個數字;轉化爲與下標相關:用下標表示(s= i+j):s(s+1)/2。即,下一斜線的第一個數字值就是s(s+1)/2

         得到了每條斜線上第一個數字的值,那麼該斜線上之後的每個值都是在這個值的基礎上依次加1得到的。

(6)下標和爲奇數時紅線的箭頭是向左下方的,且下標i是從0依次增大的;下標和爲偶數時箭頭是朝右上方的,且下標j是從0依次增大的。因此某斜線上的數字a[i][j]可以表示如下:當s % 2 != 0,即下標和爲奇數時,表示爲s(s+1)/2 + i;當s%2 ==0,即下標和爲偶數時,表示爲s(s+1)/2 + j

因此某個位置上的值爲s(s+1)/2 + ((i + j)%2 != 0 ? 0:1)

(7)當下標和s 1等於輸入的n - 1時,就是矩陣的對角線,這個作爲左上部分結束的條件

左下部分

        左下部分並不符合右上部分的規律,但該部分的規律也是十分明顯的。該部分的圖如下所示(在右側同時給出了圖2用於對比),圖中顯示的該部分相對於n* n所應該減去的值,這裏n*n=7*7= 49。例如最左側第一條斜線上數字48 = n*n - 1,所以圖3該位置標爲-1,這是爲了方便找規律。

左下部分從最左側的斜線開始,也符合以下幾個規律:

(1)該部分第一條斜線也是由一個元素組成,第二條斜線是由兩個元素組成,……

(2)相對於n*n所減去的值不斷加1,因此,該部分每個數字值都是n*n減去某個有規律的數

根據右上部分的規律,也從只有一個元素的斜線來分析該部分:

下標與斜線條數關聯:

第s條(s大於等於0)斜線與下標i和j的關係:s=2n - (i + j) - 2。例如上圖左下部分最長的斜線爲第5條斜線:5 = 2*7 -  7 - 2。

 那麼第s條斜線左方所有的斜線上的數字個數之和D爲D = 1+2+……+s = s(s+1)/2

第s條斜線上的第一個元素減去的值爲s(s+1)/2 + 1,即D+1,第2個數爲D+2,第k個數是D+k,即某斜線第k個數上減去的值爲D + i,如何k跟下標發生關係呢?在右上部分時,這個1直接是與i或j相同,但這裏分析圖1並不能直接與i或j相關,分析可以得到k可以用k = n - ((i + j)%2 != 0 ? i : j)來表示。

所以某個位置上相對於n*n減去的值是D +k = s(s+1)/2 + n - ((i + j)%2 != 0 ? i : j),因此某個位置上的值爲n*n - (D + k) = n*n - [s(s+1)/2 + n - ((i + j)%2 != 0 ? i : j)]。


代碼如下:

  1. #include <stdio.h>  
  2. #include <iostream>  
  3. using namespace std;  
  4.   
  5. int **printnxn(int **pSpace,int N) {  
  6.     int s,i,j;  
  7.     int squa;  
  8.     pSpace = (int **)malloc(N*sizeof(int *));        
  9.     if(pSpace == NULL)  
  10.         return 0;  
  11.     for(i=0; i<N; i++) { //注意這裏的內存分配和釋放  
  12.         if ((pSpace[i] = (int *)malloc(N*sizeof(int))) == NULL) {  
  13.             while (--i>0) {  
  14.                 free(pSpace[i]);  
  15.             }  
  16.             free(pSpace);  
  17.             return 0;  
  18.         }  
  19.     }  
  20.     squa = N*N;  
  21.     for (i=0; i<N; i++) {  
  22.         for (j=0; j<N; j++) {  
  23.             s = i + j;  
  24.             if (s<N)//解決右上部分,判斷條件是下標和i+j小於輸入的n  
  25.             {  
  26.                 pSpace[i][j] = s*(s+1)/2 + (((i+j)%2 != 0) ? i : j);  //註釋A  
  27.             }   
  28.             else//解決右下部分  
  29.             {  
  30.                 s = (N-1-i) + (N-1-j);  
  31.                 pSpace[i][j] = squa - s*(s+1)/2 -(N-(((i+j)%2 != 0)? i : j)); //註釋B  
  32.             }  
  33.         }  
  34.     }  
  35.     for (i=0;i<N; i++) {  
  36.         for (j=0; j<N; j++) {  
  37.              printf("%6d",pSpace[i][j]);  
  38.         }  
  39.         printf("\n");  
  40.     }  
  41.     return pSpace;  
  42.  }  
  43.   
  44. int main() {      
  45.     int N;  
  46.     scanf("%d",&N);  
  47.     int **p;  
  48.     printnxn(p,N);  
  49. }  


注:《程序員面試寶典》第93頁該題的代碼中,與上面代碼註釋A、B處的兩行代碼對應位置用了“==”,會得到下圖所示的錯誤結果,與該書代碼前邊註釋給出的示例數組不符。正確的應爲上面代碼註釋A、B處那樣爲“!=”。上述文字已經給出了詳細分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章