二次元配列を回転して表示させよう

この記事はC言語演習問題集の問題の回答です。まだ問題読んでないという方は下記記事の「二次元配列を回転して表示させよう」をぜひ見てから読んでください。

C言語練習問題のアイキャッチC言語応用問題集

解答例

2次元配列を回転して表示させるプログラム例は下記のようになります。

#include <stdio.h>

#define M 5 /* 列数 */
#define N 2 /* 行数 */

int main(void){

  int m;
  int n;

  int a[N][M] = {
    {1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10}
  };

  /* 回転なし */
  printf("回転角度:0度\n");

  for(n = 0; n < N; n++){
    for(m = 0; m < M; m++){
      printf("%d " , a[n][m]);
    }
    printf("\n");
  }
  printf("\n");

  /* 90度回転 */
  /* 行数と列数が入れ替わる */
  printf("回転角度:90度\n");

  for(n = 0; n < M; n++){
    for(m = 0; m < N; m++){
      printf("%d " , a[N - 1 - m][n]);
    }
    printf("\n");
  }
  printf("\n");

  /* 180度回転 */
  printf("回転角度:180度\n");

  for(n = 0; n < N; n++){
    for(m = 0; m < M; m++){
      printf("%d " , a[N - 1 - n][M - 1 - m]);
    }
    printf("\n");
  }
  printf("\n");

  /* 270度回転 */
  /* 行数と列数が入れ替わる */
  printf("回転角度:270度\n");

  for(n = 0; n < M; n++){
    for(m = 0; m < N; m++){
      printf("%d " , a[m][M - 1 - n]);
    }
    printf("\n");
  }
  printf("\n");

  return 0;
}

表示例

実行すると下記のように表示されます。90度、180度、270度回転して表示されていることが確認できると思います。

回転角度:0度
1 2 3 4 5 
6 7 8 9 10 

回転角度:90度
6 1 
7 2 
8 3 
9 4 
10 5 

回転角度:180度
10 9 8 7 6 
5 4 3 2 1 

回転角度:270度
5 10 
4 9 
3 8 
2 7 
1 6 

スポンサーリンク

解説

プログラムとしては、配列にアクセスを行い、アクセスした順に数字を出力していくものになっています。そして、そのアクセスする順序を変更することで、配列の回転を実現しています。

頭で考えると難しいので図を書いて、配列にどういう順でアクセスしていくかをみてみましょう。

0度回転

まずは一番わかりやすい0度からみていきましょう。回転後の配列(0度回転なので実際には回転はしない)の並びは下のようになります。

0度回転時の配列の並び

したがって、この並び順で表示するためには、配列 a へ下図の順でアクセスし、この順で数値を出力すれば良いです。

0度回転時の配列へのアクセス順序

回転なしの場合はまずは配列の一番左上から右方向に一つずつアクセスして出力し、右端まで出力したら、1つ下の要素の左端に移動。さらにこれを配列の一番下の要素を出力し終わるまで繰り返すことで表示することが可能です。

プログラムで書くと下のようなループ文になります。

  for(n = 0; n < N; n++){
    for(m = 0; m < M; m++){
      printf("%d " , a[n][m]);
    }
    printf("\n");
  }
  printf("\n");

90度回転

90度回転の場合、回転後の配列 a の並びは下のようになります。

90度回転時の配列の並び

したがって、この並び順で表示するためには、配列 a へ下図の順でアクセスし、この順で数値を出力すれば良いです。

90度回転時の配列へのアクセス順序

90度回転の場合は配列 a の左下からアクセスして出力するようになります。そこから上方向に移動して、上端に来たら右に方向に一つ移動して下から出力するのを繰り返します。

これを行うためのポイントは二つあります。

一つ目はアクセス方向が下ではなく上方向であることです。0度の時は上から下方向に移動していましたがここでは反対向きに移動させる必要があります。配列の添字として具体的に考えると、通常の反対向きの順でアクセスするためには、N – 1 から 0 まで 1 ずつ減らしながらアクセスします。

ポイントの二つ目は、2重ループのうち、内側のループは横方向ではなく縦方向のループになることです。これにより、まず縦方向にアクセスし、1列分アクセス完了後に横方向へ移動することが可能になります。これはループ文ではまず内側のループが実行されますためです。ですので、内側のループに用いる識別子を配列の縦方向の添字として考える必要があります。

これら二つのポイントを踏まえて作成したループ文が下記です。

ループの内側でインクリメントしている m が、配列 a の縦方向へアクセスする添字になっていること、縦方向へアクセスする添字は N – 1 から減らすようにすることで上方向への移動を実現していることに注目してください。

  for(n = 0; n < M; n++){
    for(m = 0; m < N; m++){
      printf("%d " , a[N - 1 - m][n]);
    }
    printf("\n");
  }
  printf("\n");

180度回転

180度回転の場合、回転後の配列 a の並びは下のようになります。

180度回転時の配列の並び

したがって、この並び順で表示するためには、配列 a へ下図の順でアクセスし、この順で数値を出力すれば良いです。

180度回転時の配列へのアクセス順序

180度回転では0度回転時の逆順でアクセスする必要があります。つまり右下から左上方向にアクセスをしていくことになります。

逆順でアクセスするために、90度回転時に説明した1つ目のポイントを考慮してプログラムを作成する必要があります。90度回転時は縦方向のみ逆順アクセスになりましたが、180度回転では縦方向だけでなく横方向も逆順にアクセスする必要があります。なのでループ文は下記のようになります。

  for(n = 0; n < N; n++){
    for(m = 0; m < M; m++){
      printf("%d " , a[N - 1 - n][M - 1 - m]);
    }
    printf("\n");
  }
  printf("\n");

270度回転

270度回転の場合、回転後の配列 a の並びは下のようになります。

270度回転時の配列の並び

したがって、この並び順で表示するためには、配列 a へ下図の順でアクセスし、この順で数値を出力すれば良いです。

270度回転時の配列へのアクセス順序

90度回転のときに説明したポイント2つ両方を考慮してプログラミングする必要があります。270度回転の場合は縦方向に関しては上から下の順(つまり順方向)にアクセスすれば良いですが、横方向に関しては右から左の順(つまり逆方向)にアクセスする必要がありますので、配列 a の2つ目の添え字は M – 1 から 0 に変化するようにする必要があります。

これらを考慮して作成したループ文が下のようになります。

  for(n = 0; n < M; n++){
    for(m = 0; m < N; m++){
      printf("%d " , a[m][M - 1 - n]);
    }
    printf("\n");
  }
  printf("\n");

独り言

こんなの簡単やろーと思って問題作ってみましたがかなり苦戦しました。個人的には二次元配列よりも1次元のポインタ上で二次元データを扱う方が得意かな…

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です