前言
全國青少年信息學(計算機)奧林匹克競賽常常要用到許多經典算法,比如約瑟夫問題、螺旋方陣、漢諾塔、八皇后問題等,而 螺旋方陣問題是其中較為常用的一種。這類問題的算法分析對于計算機圖形學、解析幾何中的相關問題都有一定的啟發性。盡管現有算法已取得了令人振奮的成績, 但依然具有一定的片面性,或者說過于復雜。實際上,這個問題有不同的解決算法,鑒于這個問題具有一定的典型性,本文針對信息學奧林匹克競賽這一問題進行了 全面系統的分析、歸納,從不同的角度對這個問題的算法進行分析并通過程序實現。使讀者對這個問題在算法選擇上有更大的余地,從而避免算法的單一性,同時對 于類似相關問題的解決也有一定的啟發和幫助。
2 問題的描述與分析
關于螺旋方陣的輸出主要是指將一些數據或符號按照一定的順序輸出到計算機的屏幕上或者是輸出到一個指定的文件中去,輸出的幾種常見情況如下圖(為簡單起見,以輸出5階的數字螺旋方陣為例):
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
1 16 15 14 13
2 17 24 25 12
3 18 25 23 11
4 19 20 21 10
5 6 7 8 9
21 22 23 24 25
20 7 8 9 10
19 6 1 2 11
18 5 4 3 12
17 16 15 14 13
21 20 19 18 17
22 7 6 5 16
23 8 1 4 15
24 9 2 3 14
25 10 11 12 13
圖1由外及里順時針;圖2由外及里逆時針;圖3由里及外順時針;圖4由里及外逆時針
在實際應用中,輸出的內容可以不盡相同。在上面的圖1至圖4中,按螺旋順序輸出的數顯然有一定的規律,而實際輸出的順序往往不是按照螺旋順序,通常是將上圖中的數據按行(或按列)輸出,因此這類問題的關鍵在于如何將有規律的數據與實際輸出時的先后順序對應起來。下面采用不同的算法來實現。
3 采用不同算法解決螺旋方陣的輸出
3.1非遞歸算法
3.1.1 “海龜”算法(順時針,由外及里)
參照海龜行走的做法,用一對變量A,B模擬海龜頭的方向,根據屏幕坐標的特點,A、B的取值和“海龜頭”方向有這樣的關系:(0,1)表示向右;(0, -1)表示向左;(-1,0)表示向上;(1,0)表示向下;用另一對變量X,Y模擬海龜位置,“海龜”每前進一步,它的新位置即為X=X+A,Y=Y+B;要海龜向右轉,就改變A、B的值,根據數學知識可以得出具體的變換公式是:C=A;A=B;B=-C。下面用自然語言對算法進行描述:讓海龜先走n步,然后右轉,再走n-1步,再右轉,再走n-1步,再右轉,再走n-2步,再右轉,再走n-2步……如此類推,直到海龜前進的步數為0時停止;而每當“海龜”前進1步,就在它位置上顯示一個數字,那么前進n步即重復執行“X:=X+A,Y:=Y+B”語句n次。但如何讓下兩個循環的重復次數都為n-1呢?解決的方法是:循環n次后,讓n的值減少0.5,然后再轉回執行同樣的循環。擴展到顯示n位數,則須留n列的位置,也就是說,海龜水平方向每次得前進n步,才有足夠的位置顯示大一點的數字方陣,需把Y=Y+B改成Y=Y+n*B就行了。“海龜”算法的優點是簡潔清晰。
3.1.2 “分割”算法(逆時針,由外及里)
設螺旋方陣對應的一般二維數組如下:
a00 a01 a02 a03 a04
a10 a11 a12 a13 a14
a20 a21 a22 a23 a24
a30 a31 a32 a33 a34
a40 a41 a42 a43 a44
圖5
按逆時針方向從外向內,一層層給下標變量賦值,由于階數n的任意性,需考慮以下幾個問題:層數k與階數n的關系式,n由用戶輸入,k根據n來計算;定義變量value,賦值時讓其自增;分析每層矩形四條邊元素的下標變化規律,將方陣元素按逆時針方向分成四個部分:方陣左半邊(三列),方陣下半邊(二行),方陣右半邊(兩列),方陣上半邊(二行)。
具體算法思想:以5階方陣為例,可判斷 k=[(n+1)/2]=3,用循環控制產生的層數,語句為for(k=0,k <(n+1)/2;k++)。
Step1:找出方陣左半邊列規律:列下標正好是層數k的值,行下標在第一列從0變到4,第二列從1變到3,在第三列從2變到2,故推導出n階螺旋方陣左半邊由外到內的列循環結構:for(i=k;i <n-k;i++) mat[i][k]=value++;此循環執行一次,產生一列元素,循環執行的次數由外循環來控制。
Step2:找出方陣下半邊行規律:行下標從4變到3,每層取值為n―k―1;列下標由外到內第一行從1變到4(a40已產生),第二行(a30 、a31已產生)從2變到3,第三行只有一個元素a22,故推導出n階螺旋方陣下半邊行循環結構:for(i=k+1;i <n-k;i++) mat[n-k-1][i]=value++;
Step3:找出方陣右半邊列規律:行下標第一列從3變化到0(a44已產生),第二列從2變到1(a43、a33、a03已產生),可推斷行的初值為n-k-2;列下標沒變化,兩列的下標分別為4、3,故推斷出右半邊的列可由下列循環結構完成:for(i=n-k-2;i >=k;i--) mat[i][n-k-1]=value++;
Step4:找出方陣上半邊行規律:已經產生了的元素不能再重新賦值,而行下標可用層次k來表示,列下標與右半邊行下標變化規律一樣,由此推斷出上半邊的行可由下列循環結構完成:for(i=n-k-2;i >k;i--) mat[k][i]=value++。
當k取一個值時,以上四個循環依序各產生一列或一行元素,由此產生一層元素,當k在變化范圍[0…(n+1)/2]內依次取值時,四個循環輪流執行,一個數字螺旋方陣就這樣生成了。
3.2 遞歸算法
分析圖五容易看出,當由外及里順時針旋轉時,最外層數據輸出后,內層的圖形只不過是層數減少了,即問題的規模變小了,但問題的性質沒有發生變化,新問題和原問題仍然可以采用相同的解法。所以這一問題完全可以采用遞歸的方法來求解。具體的算法描述如下。
Step1:初始化。把需要輸出的數據放入一維數組,設為b[1..n*n]。因為是n階方陣,所以全部元素共有n2個,輸出b[1]到b[n*n]的順序是方陣順時針旋轉的順序。
Step2:把b數組中的每個元素放入到二維數組a[i][j](圖5)中去,如b[1]放入a[0][0]中,b[2]放入a[0][1]中,……,b[6]放入a[1][4]中……,其它元素依次放入。
Step3:按行輸出數組元素a[i][j]即可。
上述算法中,難點在于如何將b數組放入到a[i][j]數組對應的分量中去。為此,對step2進行求精并使用遞歸來解決。具體做法:將數組a初始化為0。設置變量direction作為方向標志。當direction為1、2、3、4時,分別表示向右、向下、向左、向上。并編寫如下的遞歸子程序walk(x,y,direction,k)。其中參數x,y表示數組的下標。k是計數器。當 k>n*n 為遞歸出口。
case direction of
{right } 1: if到右邊界then 向下拐 else 向右輸出;
{down} 2: if到下邊界 then 向左拐 else 向下輸出;
{left} 3: if到左邊界 then 向上拐 else 向左輸出;
{up} 4: if 到上邊界 then 向右拐 else 向上輸出。
end;{end case}
3.3 算法實現
限于篇幅,本文僅給出遞歸算法的主要程序段(pascal語言)
procedure walk(x,y,direction,k:integer);
begin
if k>n*n then 按行輸出a數組;
a[x,y]:=b[k];
case direction of
{right}1: if (y=n) or (a[x,y+1]<>0) then walk(x+1,y,2,k+1) else walk(x,y+1,1,k+1);
{down}2: if (x=n) or (a[x+1,y]<>0) then walk(x,y-1,3,k+1) else walk(x+1,y,2,k+1);
{left} 3: if (y=1) or (a[x,y-1]<>0) then walk(x-1,y,4,k+1) else walk(x,y-1,3,k+1);
{up} 4: if (x=1) or (a[x-1,y]<>0) then walk(x,y+1,1,k+1) else walk(x-1,y,4,k+1)
end;
begin{main}
fillchar(a,sizeof(a),0);
writeln('please input a integer for n:');
readln(n);
walk(1,1,1,1);
end.{end main}
4 結束語
關于螺旋方陣的輸出算法主要有上述幾種,其他幾種方陣的輸出,可以仿照上述的算法分析加以實現。相對而言,遞 歸算法較為簡潔,但是時間復雜度要高一些,對于輸出高階螺旋方陣時,時間很長。另外在空間復雜度方面,采用數組這種數據結構,顯然有一定的局限性,如果使 用鏈表來存儲,將會盡可能地避免空間不足的現象。另外,上述問題也可以使用一個模板式的子程序來完成,這時要求輸入的參數要包括:從外還是從里螺旋、順時 針還是逆時針,起點坐標等參數,對于從里向外螺旋時,還要考慮螺旋方陣的階數是奇數還是偶數,分別給予不同的處理。
本篇文章來源于 義烏信息技術教研網 原文鏈接:http://www.ywhs.net/itedu/html/aosaifudao/mingshifudao/20071125/47.html