配列の最大値
配列の最大値 とは
配列の中から一番大きい値を探そう。仮の最大値を更新していくアルゴリズムを身につける。本レッスンでは、配列の最大値 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
配列の中から最大値を探す
配列 (array) の中から「いちばん大きい数」を見つける、というのはプログラミング世界で繰り返し登場する超定番のお題です。テストの最高得点、商品の最高価格、気温の最高気温など、現実の世界でも「最大値」を求める場面はあちこちにあります。
一見すると人間は一瞬で「
9が最大」と見抜けますが、コンピュータは前から順番に 1 つずつしか値を見られません。そこで「仮の最大値」をひとつ持ち歩いて、もっと大きい数に出会ったら更新していく、という地味な作業を全要素ぶん繰り返します。これは前のレッスンでやった累積パターンのいとこのような考え方で、走査パターン (走り抜けながら何かを記録するパターン) と呼ばれます。
このレッスンでは、int[] の配列を受け取り、その中の最大値を返す arrayMax メソッドを書きながら、走査パターンを体に染み込ませていきます。一度しっかり身につけてしまえば、最小値、合計、平均、特定の値を探す、など似た形の問題がぜんぶ書けるようになります。
アルゴリズムの考え方
最大値を求める手順は、ざっくり次のとおりです。
- 準備 — 仮の最大値
maxを配列の先頭要素で初期化する - 走査 —
i = 1から最後までarr[i]を見ていき、arr[i] > maxならmax = arr[i]で上書きする - 完成 — ループが終わった時点で、
maxが配列全体の最大値になっている
ここで「なぜ max を arr[0] で初期化するの? 0 じゃダメ?」と疑問に思った人はとても鋭いです。後の「よくある間違い」で詳しく扱いますが、max = 0 で始めると [-1, -5, -3] のような 全部マイナス の配列で答えを間違えます。最大値が 0 より大きいとは限らないからです。
図にすると、max の中身が走査が進むにつれてどう更新されていくかが見えてきます。下の図は [3, 7, 2, 9, 5] を走査するときの状態遷移です。
配列を 1 周だけ回って答えが出るので、計算量は要素数 n に対して O(n) です。要素が 100 万個でも 1 周で終わるので、配列処理の中ではいちばん軽い部類に入ります。
Java で配列を扱う基本
本題に入る前に、Java の配列の基本構文を軽くおさらいしておきましょう。
Java
int[] arr = {3, 7, 2, 9, 5};
int n = arr.length; // 要素数 → 5
int first = arr[0]; // 先頭要素 → 3
int last = arr[n - 1]; // 末尾要素 → 5要素数は arr.length で取れます。String の .length() (カッコ付き) と違って、配列は .length (カッコなし) なので注意してください。これは Java 初心者が必ず一度はやらかすミスです。
配列の添字 (index) は
0から始まります。要素数が5の配列なら、arr[0]からarr[4]までです。arr[5]にアクセスするとArrayIndexOutOfBoundsExceptionという、初心者には呪文のように見える例外が飛んできます。
実際に書いてみる ─ 仮の最大値で始める
それでは Solution.arrayMax(int[] arr) を実装してみます。仮の最大値を arr[0] で初期化する素直な書き方です。
Java
public class Solution {
public static int arrayMax(int[] arr) {
if (arr.length == 0) {
return 0;
}
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
}コードを読み下すと「空っぽなら 0 を返す。そうでなければ max を先頭の値にしておき、i を 1 から最後まで動かしながら、max より大きい数に出会ったら max を更新し、最後に max を返す」となります。i = 1 から始めているのは、arr[0] はもう max に入っているのでわざわざ自分自身と比べる必要がないからです。i = 0 から回しても結果は変わりませんが、ひと手間ぶん無駄になります。
別解 ─ Integer.MIN_VALUE で初期化する
もうひとつ典型的な書き方が、max を Integer.MIN_VALUE で初期化する方法です。これは int 型がとりうる最小の値 (約 マイナス 21 億) なので、「どんな配列の要素でも必ずこれより大きい」が保証されます。
Java
public class Solution {
public static int arrayMax(int[] arr) {
if (arr.length == 0) {
return 0;
}
int max = Integer.MIN_VALUE;
for (int v : arr) {
if (v > max) {
max = v;
}
}
return max;
}
}ここではついでに for-each (拡張 for 文) の書き方も使っています。for (int v : arr) は「arr の各要素を順番に v に入れてループする」という意味で、添字 i を自分で管理する必要がありません。「要素そのもの」だけ見ればよい場面では、for-each のほうがコードが短く読みやすくなります。
Integer.MIN_VALUEの反対側にInteger.MAX_VALUE(約 プラス 21 億) もあります。最小値 を求めるときは、min = Integer.MAX_VALUE;で初期化して、if (arr[i] < min)で更新します。最大値と完全に対称の関係です。
よくある間違い
配列の最大値を求めるとき、初心者がよくハマる落とし穴を 3 つ紹介します。
max = 0で初期化して負の数で失敗 —int max = 0;で始めると、配列が[-1, -5, -3]のように 全要素がマイナス のとき、答えが0になってしまいます。実際の最大値は-1なのに、です。maxの初期値はarr[0]またはInteger.MIN_VALUEにするのが鉄則です- 空配列で
IndexOutOfBoundsException—int max = arr[0];を書いた直後に、空配列[]が渡されると、arr[0]にアクセスした瞬間に例外が飛びます。必ずif (arr.length == 0) return 0;のような 空配列ガード を先頭に置いてください >=と>の違い — 更新条件をarr[i] >= maxと書いても答え自体は同じになりますが、>=だと「同じ値」に出会うたびに上書きが走るので、わずかに余計な代入が発生します。>で「より大きいときだけ更新する」のが教科書的な書き方です
もうひとつ地味に多いミスが、arr.length を arr.length() と書いてしまうケースです。String の length() と混同しがちですが、配列はカッコなし、String はカッコあり、と覚えてください。コンパイラに「cannot find symbol」と怒られたら、ここを真っ先に疑いましょう。
エラーメッセージの読み方のコツは「行番号 → エラー種別 → 変数名」の順番に追うことです。
ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5なら、「長さ5の配列にarr[5]でアクセスした」と読めます。配列は0から始まるので、最後はarr[length - 1]まで、と思い出せれば 9 割解決です。
やってみよう
それでは右側のエディタで、Solution.arrayMax(int[] arr) を完成させてみましょう。次の手順で進めるとスムーズです。
- メソッドの先頭で
if (arr.length == 0) return 0;の空配列ガードを書く int max = arr[0];で仮の最大値を初期化するfor (int i = 1; i < arr.length; i++)のループを書く- ループの中で
if (arr[i] > max) max = arr[i];で更新する - ループの後ろで
return max;を書いて、実行ボタンを押す
テストが緑になったら、for-each 版 (for (int v : arr)) や Integer.MIN_VALUE 初期化版に書き換えて、すべてのテストが同じように pass することを確認してみてください。「同じ問題を別の書き方で解く」のは、自分の引き出しを増やす最高のトレーニングです。
さらに余力があれば、Solution.arrayMin(int[] arr) という最小値を返すメソッドも自分で書いてみると、走査パターンが完全に体に染みつきます。Java の配列処理は、合計 平均 最大 最小 カウント 検索 と、ほぼ全部がこの「走査しながら 1 つの変数を育てる」形に帰着します。ここで土台を固めておけば、これから先のレッスンがぐっと楽になります。
よくある質問
Q. Java と他言語の文法はどう違いますか?
A. Java は静的型付けで、変数宣言時に型を明示します(int x = 0;)。中括弧でブロックを表し、文末にセミコロンが必要です。Python の動的型付けや JavaScript の柔軟さに慣れていると最初は窮屈ですが、コンパイル時にバグが発見できる安全性がメリットです。
Q. コードが動かないときに最初に見るべき場所は?
A. コンパイルエラーは行番号とエラー種別(cannot find symbol、incompatible types など)が表示されます。実行時例外はスタックトレースの最初の at ... が原因行です。IDE(IntelliJ / VS Code)の警告も丁寧に潰すと、半分のバグは未然に防げます。
Q. Java の習得後に学ぶべき技術は何ですか?
A. 基本構文を抑えたら java-intermediate(コレクション / ジェネリクス / Stream / 例外)に進み、Spring Boot や Web 開発に展開するのが王道です。クラウド時代は Kotlin / Scala への展開も視野に入りますが、まずは Java 標準を固めるのが効率的です。
次のレッスン
次は 配列の線形探索 で、配列の中から一番大きい値を探そう を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 最大値 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 最大値 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- クラス名は
Solution、メソッド名はarrayMax、引数はint[] arrひとつにすること - 戻り値の型は
intで、配列の中の最大値を返すこと - 配列が空 (
arr.length == 0) のときは0を返し、負の数だけの配列でも正しく動くこと
入出力例
test-cases.txt
arrayMax([3,7,2,9,5]) → 9
arrayMax([-1,-5,-3]) → -1
arrayMax([]) → 0
arrayMax([42]) → 42
arrayMax([1,2,3,4,5]) → 5
arrayMax([10,3,8,1,6]) → 10