轉載自: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)]。
代碼如下:
- #include <stdio.h>
- #include <iostream>
- using namespace std;
- int **printnxn(int **pSpace,int N) {
- int s,i,j;
- int squa;
- pSpace = (int **)malloc(N*sizeof(int *));
- if(pSpace == NULL)
- return 0;
- for(i=0; i<N; i++) { //注意這裏的內存分配和釋放
- if ((pSpace[i] = (int *)malloc(N*sizeof(int))) == NULL) {
- while (--i>0) {
- free(pSpace[i]);
- }
- free(pSpace);
- return 0;
- }
- }
- squa = N*N;
- for (i=0; i<N; i++) {
- for (j=0; j<N; j++) {
- s = i + j;
- if (s<N)//解決右上部分,判斷條件是下標和i+j小於輸入的n
- {
- pSpace[i][j] = s*(s+1)/2 + (((i+j)%2 != 0) ? i : j); //註釋A
- }
- else//解決右下部分
- {
- s = (N-1-i) + (N-1-j);
- pSpace[i][j] = squa - s*(s+1)/2 -(N-(((i+j)%2 != 0)? i : j)); //註釋B
- }
- }
- }
- for (i=0;i<N; i++) {
- for (j=0; j<N; j++) {
- printf("%6d",pSpace[i][j]);
- }
- printf("\n");
- }
- return pSpace;
- }
- int main() {
- int N;
- scanf("%d",&N);
- int **p;
- printnxn(p,N);
- }
注:《程序員面試寶典》第93頁該題的代碼中,與上面代碼註釋A、B處的兩行代碼對應位置用了“==”,會得到下圖所示的錯誤結果,與該書代碼前邊註釋給出的示例數組不符。正確的應爲上面代碼註釋A、B處那樣爲“!=”。上述文字已經給出了詳細分析。