【例題】配列の要素を小さい順(昇順)に並べ替えよう

例題として、配列内の数値を小さい順(昇順)に並べ替えるプログラムについて考えてみましょう。
sortArraySmall関数を作成し、配列を並べ替えさせてみましょう。

動かしてみよう

サンプルコードを動かしてみましょう。
実行ボタンを押してみてください。
また int input[] の { } 内を色々と変えての実行もしてみてください。

input配列の中身が小さい順に並べ替えられたことが、出力結果から確認できたと思います。
では、解説に進みましょう。

解説

サンプルコードの要点について説明します。

/* プロトタイプ宣言 */
void sortArraySmall(int array[], int size);
void swap(int *pa, int *pb);

毎度おなじみ、関数のプロトタイプ宣言です。
『XXXX関数っていうのがあるよ。』と宣言するために必要な記述でした。
また今回は配列内の値を交換する処理をおこなう必要があるのですが、その処理については以前に【例題】2つの変数の値を交換させようで作成したswap関数を流用しますので、swap関数のプロトタイプ宣言もここでおこなっています。

続いてmain関数内の下記。

int input[] = { 2, 5, -1, 0 };
int size = sizeof(input)/sizeof(int);

配列inputと変数sizeを作成しています。
変数名の後ろに[]をつけることで、その変数を配列にすることができます。
また[]内を空にしておくことで、{}内の数から自動的に配列の要素数が決定されます。
sizeの初期化はsizeof演算子を用いておこなっています。
そこについての説明は【例題】配列の平均値を求めようにておこなっていますので、必要に応じて参照してください。

参考までに、サンプルコードの変数宣言部は下記と同じ意味です。

int input[4] = { 2, 5, -1, 0 };
int size = 4;

続いて、sortArraySmall関数のコール部分です。

sortArraySmall( input, size );

第1引数ですが、inputと書くことで、input配列の先頭アドレスを sortArraySmall関数に渡しています。

int input; と定義された変数の場合、そのアドレスは &input で表現されます。
int input[]; と定義された配列の場合、その先頭アドレスは input もしくは &input[0] で表現されます。

また第2引数では配列のサイズをsortArraySmall関数へ渡しています。

引数はinput配列だけじゃダメなの?
・・・第1引数によって関数へ渡されるのは配列の先頭アドレス(メモリ上のどこに配列があるか)だけです。
先頭アドレスから何個分、input配列の値があるかはsortArraySmall関数からは知ることができないので、第2引数のsizeで知らせてあげましょう。

続いて、sortArraySmall関数の中身についてです。
内側のループから見てみましょう。

for( int j = 1; j < size; j++ )
{
    if( array[j-1] > array[j] )
    {
        swap( &array[j-1], &array[j] );
    }
}

内側のループ処理では大きい数値を右端に移動させる、という動作を実現しています。
int input[] = { 2, 5, -1, 0 };の時の動作をみてみましょう。

/* 内側のループ1週目 (j=1) { 2, 5, -1, 0} */
if( array[0] > array[1] ) /* 2 と 5 を比較 */
{
/* if不成立のため、ここは実行されない */
swap( &array[0], &array[1] );
}

/* 内側のループ2週目 (j=2) { 2, 5, -1, 0} */
if( array[1] > array[2] ) /* 5 と -1 を比較 */
{
/* 5 が大きいので -1 と位置を交換 { 2, -1, 5, 0} */
swap( &array[1], &array[2] );
}

/* 内側のループ3週目 (j=3) { 2, -1, 5, 0} */
if( array[2] > array[3] ) /* 5 と 0 を比較 */
{
/* 5 が大きいので 0 と位置を交換 { 2, -1, 0, 5} */
swap( &array[2], &array[3] );
}

内側のループを1回実行することで、1番大きな値である5を右端に持ってくることができました。

あとはこの処理を複数回繰り返すことで、配列全体を小さい順に並び替えることができます。
そのために実装されている箇所が外側のfor文です。

for( int i = 1; i < size; size--)

size-- をおこなわせているのは、内側のループの処理の対象となる配列要素数を減らすためです。
内側のループをおこなうことで一番右端に一番大きな値が移動することは先ほどの説明のとおりですので、2回目のループにおいては2番目に大きい値を右から2番目に移動させることになります。
そのため、一番右端は並び替えの対象外としてかまいませんので、外側のループのカウント処理では size-- をおこなっています。

処理のイメージは下記です。

/* 外側のループ1週目 (size = 4) */
{ 2, 5, -1, 0 } の中の大きい値を右端に寄せる。
→{ 2, -1, 0, 5 }

/* 外側のループ2週目 (size = 3) */
{ 2, -1, 0 } の中の大きい値を右端に寄せる。
→{ -1, 0, 2 }

/* 外側のループ3週目 (size = 2) */
{ -1, 0 } の中の大きい値を右端に寄せる。
→{ -1, 0 }

これらの処理の結果を合わせると、{ 2, 5, -1, 0 } が { -1, 0, 2, 5 } となります。

次にswap関数ですが、これについては別記事(【例題】2つの変数の値を交換させよう)の方で詳しく説明していますので、そちらを参照ください。

以上です。
ではまた。

タイトルとURLをコピーしました