問題描述
回形取數就是沿矩陣的邊取數,若當前方向上無數可取或已經取過,則左轉90度。一開始位於矩陣左上角,方向向下。
輸入格式
輸入第一行是兩個不超過200的正整數m, n,表示矩陣的行和列。接下來m行每行n個整數,表示這個矩陣。
輸出格式
輸出只有一行,共mn個數,爲輸入矩陣回形取數獲得的結果。數之間用一個空格分隔,行末不要有多餘的空格。
樣例輸入
3 3
1 2 3
4 5 6
7 8 9
樣例輸出
1 4 7 8 9 6 3 2 5
樣例輸入
3 2
1 2
3 4
5 6
樣例輸出
1 3 5 6 4 2
我設計了三種算法來解決此道題目,並經過對算法的分析,來看該三種算法的優劣。算法
1. 算法設計:(遞歸法)
矩陣由四個邊組成,回型取數在不一樣的邊上取數方向不一樣,所以能夠分爲四種狀況來取數。經過一個數s取餘4來對應四個狀態,經過遞歸算法來輸出每一個數,當每邊的數取完時就使s加一來取另一邊的數(if...else..實現)。
遞歸時傳參傳的是每一個數的行列值。例如:
當取完a【i】【j】時,若s=0時,對應取的是左邊即向下取數,則傳參數solve(i+1,j);若s=3時,對應取的是上邊即向左取數,則傳參數solve(i,j-1)。
ide
--學習
程序代碼以下設計
#include <stdio.h> #include <string.h> #define N 10 #define M 10 int s=0; int m,n; int a[M][N],b[M][N]; void solve(int i,int j){ if(i>=0&&i<m&&j>=0&&j<n&&b[i][j]==0) {printf("%d ",a[i][j]); b[i][j]=1; } else s++; if (s%4==0) solve(i+1,j); if(s%4==1) solve(i,j+1); if(s%4==2) solve(i-1,j); if(s%4==3) solve(i,i-1); } int main() { int i,j; scanf("%d%d",&m,&n); memset(b,0,sizeof(b)); for(i=0;i<m;i++) { for (j=0;j<n;j++) scanf("%d",&a[i][j]); printf("\n"); } solve(0,0); return 0; }
二、算法設計:逐圈分析分別處理每圈的左側、下方、右方、上方的數據。先計算可分爲幾圈,因爲每轉一圈行上的個數會減小2個,所以看能夠減小几個2就有幾圈,用行數除以2可算出有幾圈。(若行數爲奇數,也是除二向下取整可舉例實驗)。
i 層內輸出數據的4個過程爲(四角元素分別歸四個邊):
(1) i 列(左側),從 i 行到m-i-1 行;
(2) m-i-1行(下方),從 i 列到 n-i -1列;
(3) n-i-1 列(右側),從 m-i-1 行到 i+1 行;
(4)i 行(上方),從 n-i-1 列到 i 列;
4個過程經過4個循環實現,用 j 表示 i 層內每邊中行或列的下標。
__
程序代碼以下:code
#include <stdio.h> #include <string.h> #define M 10 #define N 10 void solve() { } int main(){ int a[M][N]; int m,n,i,j; scanf("%d%d",&m,&n); for(i=0;i<m;i++) for(j=0;j<n;j++) scanf("%d",&a[i][j]); //開始取數 for(i=0;i<m/2;i++) { for(j=i;j<m-i-1;j++) //左側 printf("%d",a[j][i]); for(j=i;j<n-i-1;j++) printf("%d",a[m-i-1][j]); //下方 for(j=m-i-1;j>i;j--) printf("%d",a[j][n-i-1]); //右側 for(j=n-i-1;j>i;j--) printf("%d",a[i][j]); //上方 } return 0; }
**三、算法設計:(算法設計數p.83)經過設置變量標識一圈中不一樣方位的處理差異,並經過算術運算將4個方位的處理歸結成一個循環過程。
經過輸出最外一圈的狀況分析:遞歸
j=1 | i=i+1 | 0~n-1 | k=n | //左側 |
---|---|---|---|---|
i=n | j=j+1 | 1~n-1 | k=n-1 | //下方 |
j=n | i=i-1 | n-2~0 | k=n-1 | //右側 |
i=1 | j=j+1 | n-2~0 | k=n-2 | //上方 |
從上面i,j 的變化可發現:輸出時,前半圈下標變化一致,都加1;後半圈都減1,不一樣的是變化範圍,因此分兩邊前半圈和後半圈,引入t=1,每半圈改變t的正負號再進行行列值改變。
前半圈再分左邊與下邊,可知前m個數是左邊,後n-1是下邊,在此引入兩值b【0】與b【1】,當第i個數取餘m等於0時則爲左邊的數,由於(i從0取因此仍是m個數)等於1則爲下邊的數。後半圈同理。。
爲表達,要統一表示循環變量的範圍,可發現當輸出到左下角時行列數少一,右上角行列數又少一,所以在進行半圈輸出後,要對行列值減一。**
——
程序代碼以下:博客
#include <stdio.h> #include <string.h> #define M 10 #define N 10 int main(){ int a[M][N]; int b[2]; int m,n,x,y,i,j; int t=1; b[0]=-1; b[1]=0; scanf("%d%d",&m,&n); for(i=0;i<m;i++) for(j=0;j<n;j++) scanf("%d",&a[i][j]); //開始取數 while(x<=m*n) {for(y=0;y<(m+n-1);y++) { b[y/m]+=t; printf("%d",a[b[0]][b[1]]); x++; } m--; n--; t=-t; } return 0; }
-----
三種算法比較及學習心得:
算法 一、2比較好理解,在思考方面能夠節約大量時間,算法也是相通的,體現了遞歸和循環的相互轉換;
算法 3 須要經過概括,構造循環不變式,寫出的算法節約了運行時的時間。
比較偏向算法1,好理解,清晰明瞭,遞歸老是用很簡單的語句實現了很複雜的過程,所以我很喜歡讀遞歸程序。
經過算法三瞭解到,要善於經過數學概括構造不變式,這也是一個寫算法很好的習慣。數學
ps:第一次寫博客,意猶未盡,以前覺得徹底掌握的在總結的時候仍是會有磕絆的地方,經過寫博客也是將該問題又踏平了很多,之後這個習慣仍是要堅持的,是提升也是個記錄與回憶吧,今天算是個好的開端吧?嘿嘿嘿。。
對了,瀏覽過有問題的話,咱們再一塊兒探討啊!string