今回は Java の配列を使った「配列を逆順に並べ替える(反転)」を完全攻略します。読みやすく段階を追っていきますね。
まずゴールを簡単に
配列 {12, 24, 18, 31, 17} を {17, 31, 18, 24, 12} のように 順番を逆にする(左右を入れ替える)処理を実装します。
※目的は「配列の並びを反転すること」です。
基本アイデア
配列の「先頭」と「末尾」を入れ替える → 次に「先頭から2番目」と「末尾から2番目」を入れ替える → 中央まで繰り返す。
つまり左右から寄せていって入れ替えるだけ。
重要な概念(初心者がまず覚えるべき)
- 配列のインデックスは 0 から始まる(例:長さ 5 の配列なら最後のインデックスは
4)。 - 2つの要素を入れ替えるときは一時変数(
temp)が必要(直接入れ替えると上書きされる)。 - 反転は インプレース(元の配列をそのまま反転) と 新しい配列を作って反転コピー(元は残す) の2パターンがある。
サンプルコード(最も基本・分かりやすい:インプレースで反転)
import java.util.Arrays;
class ReverseExample {
public static void main(String[] args) {
int[] src = {12, 24, 18, 31, 17};
System.out.println("元: " + Arrays.toString(src));
// f = front index(先頭), l = last index(末尾)
for (int f = 0, l = src.length - 1; f < l; f++, l--) {
int temp = src[f]; // 先頭の値を退避
src[f] = src[l]; // 末尾の値を先頭へコピー
src[l] = temp; // 退避しておいた先頭の値を末尾へコピー
}
System.out.println("反転後: " + Arrays.toString(src));
}
}
Java- 実行結果:
- 元:
[12, 24, 18, 31, 17] - 反転後:
[17, 31, 18, 24, 12]
- 元:
行ごとのやさしい説明(上のコード)
int[] src = { ... }:配列を作る。src.length - 1:配列の最後のインデックス(長さが5なら4)。for (int f = 0, l = src.length - 1; f < l; f++, l--):先頭をf、末尾をlとして、fがlより小さい間だけループ。ループ内でfは右へ、lは左へ進む。int temp = src[f];:fの値を一時的に保存(退避)。src[f] = src[l];:lの値をfに入れる。src[l] = temp;:退避しておいたfの値をlに入れる(これで入れ替え完了)。
図で理解(配列長 5 の場合)
初期: indexes → 0 1 2 3 4
値 → 12 24 18 31 17
1回目(f=0,l=4)入れ替え:
- swap src[0] と src[4]
結果 → 17 24 18 31 12
2回目(f=1,l=3)入れ替え:
- swap src[1] と src[3]
結果 → 17 31 18 24 12
終了(f=2,l=2): 中央になったのでこれ以上交換しない(f < l が false)。
計算量(効率)
- 時間計算量:O(n/2) → 通常は O(n)。(配列の半分だけ交換するため)
- 空間計算量:O(1)。追加の配列を使わず、定数個の変数だけ使う。
別パターン:新しい配列を作って反転コピー(元は残す)
int[] src = {12, 24, 18, 31, 17};
int[] dst = new int[src.length];
for (int i = 0; i < src.length; i++) {
dst[i] = src[src.length - 1 - i];
}
// dst は反転された配列、src は元のまま
Java- 使いどころ:元の配列を保持したいとき(非破壊的)。
応用例:String[] を反転
同じロジックで String[] でも動きます。
String[] names = {"Alice","Bob","Carol","Dave"};
for (int f=0, l=names.length-1; f<l; f++, l--) {
String t = names[f];
names[f] = names[l];
names[l] = t;
}
Javaよくあるミス(初心者が陥りやすい)
- ループ条件を
f <= lにしてしまう → 中央要素と自分自身を交換する(無害ですが不要)。ただし参照型であれば余計な操作になる。 tempを使わずにsrc[f] = src[l]; src[l] = src[f];のように書いてしまう → 上書きされて元の値が失われる(バグ)。- インデックスの計算を間違える(
src.lengthとsrc.length - 1の混同)。 - 範囲を部分的に反転したいときに境界を間違える(例:
fromとtoをどう指定するか)。
部分だけを反転する(応用)
配列の一部 [from, to](インデックス)だけ反転したいとき:
void reverseRange(int[] a, int from, int to) {
for (int f = from, l = to; f < l; f++, l--) {
int t = a[f];
a[f] = a[l];
a[l] = t;
}
}
Java呼び出し例:reverseRange(arr, 1, 3); → インデックス 1〜3 が反転される。
練習問題 — 解答付き
問題1int[] a = {1,2,3,4} をコードで反転するとどうなる?(手でシミュレート)
解答1
反転後 → {4,3,2,1}
問題2String[] s = {"A","B","C"} に先ほどの in-place アルゴリズムを適用したら最終的にどうなる?
解答2
反転後 → {"C","B","A"}
問題3(少しチャレンジ)
長さ 6 の配列 {10,11,12,13,14,15} の from=1,to=4 の部分だけ反転したら最終的な配列は?
解答3
部分 [1..4](11,12,13,14)を反転 → 14,13,12,11
最終配列 → {10,14,13,12,11,15}
まとめ(初心者が覚えるべき要点)
- 反転は左右から寄せて入れ替えるだけ。
fとlを使う。 - スワップには一時変数
tempが必要。 - インプレース(破壊的)とコピーして作る(非破壊的)の2パターンがある。
- ループは
for (f=0, l=n-1; f<l; f++, l--)が定番。 - 部分反転や型を変えた応用も簡単にできる。
