【C言語】配列の中から最大値を持つ要素の”添字”を求める

C言語での配列の中の最大値を持つ要素の添字を求める方法の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、C言語で配列の中から「最大値を持つ要素の添字」を求める方法について解説していきます。

下記ページではC言語での最大値の求め方について解説していますが、今回はその応用として「単なる最大値」ではなく、「最大値を持つ要素の添字」を求めていきます。

C言語における最大値と最小値の求め方の解説ページアイキャッチ 【C言語】最大値と最小値を求める方法

といっても、基本的な考え方は最大値を求める時と同じです。逆にいうと最大値を求める方法をご存知なければ「最大値を持つ要素の添字」を求めるのが難しいため、もし最大値の求め方をご存知ない方は、上記ページに事前に目を通しておいていただいた方が良いと思います!

それでは、配列の中から「最大値を持つ要素の添字」を求める方法について解説していきます!

MEMO

このページでは「最大値」を持つ要素の添字を求める関数を紹介していきますが、「最小値」を持つ要素の添字を求めたい場合は、比較演算子 >< に、>=<= にそれぞれ置き換えてください

最大値を持つ要素の添字の求め方

下記ページでも解説しているとおり、

C言語における最大値と最小値の求め方の解説ページアイキャッチ 【C言語】最大値と最小値を求める方法

配列の中から単に最大値を求める場合、まず配列のいずれかの要素の値を最大値と仮定します。

配列の中から最大値を求める手順1

さらに、配列の先頭から順に要素の値と最大値とを比較し、その要素の値が最大値を超える場合は、最大値をその要素の値に更新します。

配列の中から最大値を求める手順2

そしてこれを配列の末尾まで繰り返し、繰り返しが完了した時点の最大値が「配列の中の最大値」となります。

配列の中から最大値を求める手順3

それに対し、「最大値を持つ要素の添字」を求める処理は、最大値を更新するタイミングで最大値を持つ要素の添字も更新するようにすることで実現することが出来ます。

より具体的には、まず配列のいずれかの要素の値を最大値と仮定すると同時に、最大値を持つ要素の添字もその要素の添字に更新します。

配列の中から最大値を持つ要素の添字を求める手順1

さらに、配列の先頭から順に要素の値と最大値とを比較し、その要素の値が最大値を超える場合は、最大値をその要素の値に更新します。そしてこのタイミングで、最大値を持つ要素の添字もその要素の添字に更新します。

配列の中から最大値を持つ要素の添字を求める手順2

そしてこれを配列の末尾まで繰り返し、繰り返しが完了した時点の最大値を持つ要素の添字が、求めるべき「配列内で最大値を持つ要素の添字」となります。

配列の中から最大値を持つ要素の添字を求める手順3

このように、最大値を持つ要素の添字は、最大値を求める関数に対して最大値を持つ要素の添字を更新する処理を追加するだけで求めることが出来ます。

最大値を持つ要素の添字を求める関数

以上を踏まえると、配列の中から最大値を持つ要素の添字を求める関数は下記の maxIndex のように記述することが出来ます。

より具体的には、maxIndex はサイズ n の配列 nums 内で最大値を持つ要素の添字を返却する関数となります。

配列の中から最大値を持つ要素の添字を求める
int maxIndex(int nums[], int n) {
    int max_value; /* 最大値 */
    int max_index; /* 最大値を持つ要素の添字 */
    int i;

    /* nums[0]を最大値と仮定する */
    max_value = nums[0];

    /* 現状の最大値の存在する要素に合わせて添字も設定 */
    max_index = 0;

    for (i = 0; i < n; i++) {
        if (nums[i] > max_value) {
            /* 最大値よりもnums[i]の方が大きければ最大値を更新 */
            max_value = nums[i];

            /* 最大値の存在する要素に合わせて添字も更新 */
            max_index = i;
        }
    }

    /* 最大値の存在する要素の添字を返却 */
    return max_index;
}

下記の最大値を求める関数 maxValue と比較すれば、最大値を持つ要素の添字を求める際にどのような処理が追加で必要になるかが分かりやすいと思います。

配列の中から最大値を求める
int maxValue(int nums[], int n) {
    int max_value; /* 最大値 */
    int i;

    /* nums[0]を最大値と仮定する */
    max_value = nums[0];

    for (i = 0; i < n; i++) {
        if (nums[i] > max_value) {
            /* 最大値よりもnums[i]の方が大きければ最大値を更新 */
            max_value = nums[i];
        }
    }

    return max_value;
}

スポンサーリンク

最大値を持つ要素が複数存在する場合の対応

先ほど紹介した maxIndex 関数で配列内の最大値を持つ要素の添字を求めることはできるのですが、最大値を持つ要素が複数存在する場合に「1番先頭側に存在する要素の添字を求める」or「1番末尾側に存在する要素の添字を求める」ことを要求される場合、もうちょっと考慮が必要になります。

最大値を持つ要素が2つ以上ある場合の例

「1番先頭側に存在する要素の添字を求める」or「1番末尾側に存在する要素の添字を求める」のどちらを実現したいかによって、処理を変更する必要があります。

配列の先頭側の添字の求め方

例えば先ほど紹介した maxIndex 関数は、最大値を持つ要素が複数存在する場合、その最大値を持つ要素の中の「1番先頭側に存在する要素の添字」を返却する作りになっています。

ですので、最大値を持つ要素が複数存在する場合に「1番先頭側に存在する要素の添字」を求めたい場合は上記の maxIndex 関数がそのまま使えます。

また、最大値を持つ要素の添字であれば「どの要素の添字でも良い」場合でも上記の maxIndex 関数がそのまま使えます。

配列の末尾側の添字の求め方

ただし、最大値を持つ要素が複数存在する場合に、その要素のうちの「1番末尾側に存在する要素の添字」を返却するようにしたい場合は、maxIndex 関数の変更が必要です。

使用している比較演算子を変更する

そのための変更方法の1つとして「比較演算子の変更」が挙げられます。

具体的には、下記のように、maxIndex 関数で使用している比較演算子を > から >= に変更することで、最大値を持つ要素が複数存在する場合に「1番末尾側に存在する要素の添字」を返却することが出来るようになります。 

最大値以上の場合にも添字を更新する
int maxIndex(int nums[], int n) {
    int max_value; /* 最大値 */
    int max_index; /* 最大値を持つ要素の添字 */
    int i;

    /* nums[0]を最大値と仮定する */
    max_value = nums[0];

    /* 現状の最大値の存在する要素に合わせて添字も設定 */
    max_index = 0;

    for (i = 0; i < n; i++) {
        if (nums[i] >= max_value) {
            /* nums[i]が最大値以上であれば最大値を更新 */
            max_value = nums[i];

            /* 最大値の存在する要素に合わせて添字も更新 */
            max_index = i;
        }
    }

    /* 最大値の存在する要素の添字を返却 */
    return max_index;
}

前述の maxIndex 関数との違いは比較演算子 > の >= への変更のみです。

比較演算子が > の場合、max_valuenums[i] が同じだと max_index が更新されません。

比較演算子が">"の時の動作

ですので、ループ終了時には最初に見つけた最大値を持つ要素の添字、すなわち同じ最大値を持つ要素のうち「1番先頭側の要素の添字」が max_index に格納されていることになります。

その一方で比較演算子が >= の場合、max_valuenums[i] が同じ時に max_indexi に更新されます。

比較演算子が">="の時の動作

ですので、ループ終了時には最後に見つけた最大値を持つ要素の添字、すなわち同じ最大値を持つ要素のうち「1番末尾側の要素の添字」が max_index に格納されていることになります。

最大値そのものを求める場合は、比較演算子が > でも >= でも結果は変わりませんが、最大値を持つ要素の添字を求める際は比較演算子が > と >= のどちらであるかで結果が変わるので注意が必要です。

比較の順序を変更する

また、比較演算子を変更するのではなく、下記のように末尾側から比較を行うように変更した場合も、最大値を持つ複数の要素のうち「1番末尾側の要素の添字」を求めることが出来ます。

比較を末尾側から行う
int maxIndex(int nums[], int n) {
    int max_value; /* 最大値 */
    int max_index; /* 最大値を持つ要素の添字 */
    int i;

    /* nums[n - 1]を最大値と仮定する */
    max_value = nums[n - 1];

    /* 現状の最大値の存在する要素に合わせて添字も設定 */
    max_index = n - 1;

    for (i = n - 1; i >= 0; i--) {
        if (nums[i] > max_value) {
            /* 最大値よりもnums[i]の方が大きければ最大値を更新 */
            max_value = nums[i];

            /* 最大値の存在する要素に合わせて添字も更新 */
            max_index = i;
        }
    }

    /* 最大値の存在する要素の添字を返却 */
    return max_index;
}

この場合は、最大値と同じ値を持つ要素を見つけても max_index が更新されないため、最初に見つけた最大値の値を持つ要素の添字が max_index に格納されることになります。配列の末尾側から比較を行なっているため、最大値を持つ複数の要素のうち「一番末尾側の要素の添字」が max_index として求まることになります。

最大値そのものを求める場合は、比較を行う順番が先頭側からでも末尾側からでも結果は変わりませんが、最大値を持つ要素の添字を求める際は比較を行う順番によって結果が変わるので注意が必要です。

仮の最大値の設定にも注意

また、上記においては、ループに入る前に max_valuemax_index へ代入する値がそれぞれ nums[n - 1] と n - 1 でなければ上手く「一番末尾側の要素の添字」を求めることが出来ないことにも注意が必要です。

MEMO

ループに入る前に max_value への値を代入する必要がある理由については下記ページの 配列の中から最大値を求める際の注意点 で解説していますので、詳しく知りたい方は下記ページを参照してください

C言語における最大値と最小値の求め方の解説ページアイキャッチ 【C言語】最大値と最小値を求める方法

例えば、上記の関数において、ループに入る前に max_valuemax_index への代入値をそれぞれ nums[0] と 0 とした場合、下記のように nums の全要素の値が同じだと nums[i] > max_value が一度も成立せず、max_index も一度も更新されないことになります。

そうなると、最終的な max_index の値はループに入る前に代入した 0 となり、先頭側の要素の添字を求めることになってしまいます。

一度もmax_indexが更新されない様子

単に最大値を求めるのであれば、ループに入る前に max_value へ代入する値は配列内の要素の値のものであればどの値でもよかったのですが、最大値の添字を求める際には、配列のどの要素の値を max_value に代入したかによって結果が変わってしまいますので注意が必要です(max_index も同様)。

もちろん、同じ最大値を持つ要素が複数存在する場合でも、最大値の要素の添字であれば「どれでも良い」のであればそこまで深く考える必要はないです。

ですが、同じ最大値を持つ要素が複数存在する場合に、先頭側の方の添字 or 末尾側の方の添字を求めることを要求された場合、求めたい位置に応じて “比較演算子” や “比較を行う方向”・”仮の最大値(max_value)や仮の最大値の添字(max_index)の設定” を適切に選ぶようにする必要があります。

スポンサーリンク

まとめ

このページでは、C言語で配列の中から「最大値を持つ要素の添字」を求める方法について解説しました!

基本的には、配列の中から最大値を求める際に、最大値を更新するタイミングで一緒に最大値を持つ要素の添字も更新するようにすれば、「最大値を持つ要素の添字」も求めることが可能です。

ただし、最大値を持つ要素が複数ある場合は、比較演算子や比較の順番等の違いによって求まる添字が「1番先頭側のものであるか」「1番末尾側のものであるか」が異なるため、その点にだけは注意してください!

オススメの参考書(PR)

C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!

まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。

  • 参考書によって、解説の仕方は異なる
  • 読み手によって、理解しやすい解説の仕方は異なる

ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?

それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。

なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。

特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。

もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!

入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。

https://daeudaeu.com/c_reference_book/

同じカテゴリのページ一覧を表示