第 13 回 - ソーティング(2)

[6/18, 2006 H.Aman]
67x27(1669bytes)   67x27(1669bytes) 【課題 3】

例題 3

5 つの文字列を読み込み,入力順に配列 a へ格納しなさい. そして,それらを辞書式順に並び替えて出力しなさい.
ただし,文字列の入力は 1 行で 1 つとし, その長さは改行を含めて 16 文字未満とする.

方針・アルゴリズム

まず,文字列を 5 つ読み込み,それぞれ配列 a へ格納する.
そして a[0], ..., a[4] を辞書式順に並び替える.
単一文字の比較と異なり,文字列の比較には strcmp 関数を利用する. いま,s1 と s2 の 2 つの文字列があり, s1 の方が s2 よりも辞書式順で先(数値でいえば小さい)とすると strcmp(s1, s2) の値が負の数になる. これを利用すれば文字列のソーティングができる.
  • n = 4 〜 1 の範囲(-1 していく)で以下を繰り返す(右端をずらす):
    • m = 0 と仮定( a[0] が辞書式で最大と仮定 )する.
    • そして,i の値を 1 〜 4 の範囲で変化させながら, strcmp(a[m], a[i]) < 0 ならば m ← i とする.
    • 最後に m < n なら a[m] と a[n] の値を交換する.
  • 並び替えた後の文字列を出力する.

コーディング例

     1	/*
     2	 * プログラミング演習 第 13 回
     3	 * [例題 3]
     4	 * (C) 2006 Hirohisa AMAN <aman@cs.ehime-u.ac.jp, aman@computer.org>
     5	 */
     6	#include <stdio.h>
     7	#include <stdlib.h>
     8	
     9	int main(void){
    10	  char a[5][16];
    11	  char tmp[16];
    12	  int n, m, i, c;
    13	
    14	  for ( c = 0; c < 5; c++ ){
    15	    printf("%d > ", c+1);
    16	    fgets(a[c], 16, stdin);
    17	  }
    18	    
    19	  for ( n = 4; n >= 0; n-- ){
    20	    m = 0;  /* a[0] が辞書式順で一番後ろと仮定 */
    21	    for ( i = 1; i <= n; i++ ){
    22	      if ( strcmp(a[m], a[i]) < 0 ){ 
    23		m = i;  /* 一番後ろ(最大)位置の更新 */
    24	      }
    25	    }
    26	    if ( m < n ){ /* a[m] と a[n] の値を交換 */
    27	      strcpy(tmp, a[m]);
    28	      strcpy(a[m], a[n]);
    29	      strcpy(a[n], tmp);
    30	    }
    31	  }
    32	
    33	  for ( c = 0; c < 5; c++ ){
    34	    printf("%s", a[c]);
    35	  }
    36	
    37	  return 0;
    38	}
     
※左端の数字は行番号であり,ソースコードには含まれない点に注意!

コンパイル & 実行例

     $ gcc example13_3.c [Enter]
     $ ./a.out [Enter]
     1 > this [Enter]
     2 > is [Enter]
     3 > a [Enter]
     4 > test [Enter]
     5 > data [Enter]
     a
     data
     is
     test
     this
     

解説

文字列の読み込みに関しては,fgets を使えばよいので説明は不要であろう.

ただ,ここで注意しなければならないのは, 文字列が 1 個ではなく複数個入力され, それらをいったん配列に入れておく必要があるという点である.

この場合,a は文字列の配列ということになる. 既に学んだように,文字列は文字(char)配列なので, 「文字列の配列」というのは 「char 配列を要素とした配列」 ということになる.

よって,宣言は
 char  a[5][16]; 
となり,a[0], a[1], ..., a[4] がそれぞれ文字列(長さ 16 未満)を格納するための char 配列となる.

文字列どうしの(辞書式順での)比較は, 上述のように strcmp 関数を使えばよい.
そして,適宜,文字列の交換を行えばよい.

ただし,文字列の交換については, 例題 2 まで行ってきたような代入が使えない. 直感的には tmp をポインタ変数として,

     tmp = a[n];
     a[n] = a[m];
     a[m] = tmp;
という具合いにこれまでやってきたのと同じ方法で a[m] と a[n] とを入れ替えればよいように思えるが, 残念ながら配列内のポインタは書き換えができない (第 11 回の解説スライド, 最後の 3 ページを参照) ため,それぞれ内容をまるごとコピーするしかない (27〜29行目):
    27	      strcpy(tmp, a[m]);
    28	      strcpy(a[m], a[n]);
    29	      strcpy(a[n], tmp);