1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java】 ~アルゴリズム徹底解説~ 配列の中で同じ要素があるかチェックする良い方法・悪い方法

Last updated at Posted at 2025-11-07

対象読者

  • アルゴリズムについて学んでいる人
  • いろんな問題に適用できる考え方を学びたい人
  • Javaを学習中の人

問題の前提条件

  • 受け取る配列の要素は int 型とする。

問題

配列の中に同じ要素があれば true を返し、なければ false を返す関数を作成せよ。


解き方パターン1:Brute Force(よくない例)

まずは単純な実装を見てみる。

// 配列の中に重複する要素があれば true を返す
// 例: nums = [1,2,3,1] の場合、結果は true
public boolean containsDuplicate(int[] nums) {
    // 特殊ケース: 要素数が1以下なら重複はないので false を返す
    if (nums.length <= 1) return false;

    // 配列をコピーして、2つの配列で二重ループを行う
    int[] copiedNums = Arrays.copyOf(nums, nums.length);
    for (int num : nums) {
        for (int copiedNum : copiedNums) {
            // 同じ要素が見つかれば true を返す
            if (num == copiedNum) {
                return true;
            }
        }
    }

    // 最後まで見つからなければ false を返す
    return false;
}

何がいけないのか

この実装では、ネストされたループが問題になる。
要素数 n の配列に対して、各要素ごとに n 回の比較を行うため、計算量は O(n²) となる。

n × n = n²

入力サイズが増えると計算時間が指数的に増加するため、このようなアルゴリズムは避けたい。
では、どうすればよいか。次のパターンで最適化を考える。


解き方パターン2:HashMapを使った最適化例

コードの全体像

// 配列の中に重複する要素があれば true を返す
// 例: nums = [1,2,3,1] の場合、結果は true
public boolean containsDuplicate(int[] nums) {
    // 特殊ケース: 要素数が1以下なら重複はないので false を返す
    if (nums.length <= 1) return false;

    // 要素の出現を記録するためのハッシュマップを作成
    Map<Integer, Boolean> map = new HashMap<>();
    
    // 配列を左から右へ一度だけ走査する
    for (int num : nums) {
        // すでに登録済みの要素であれば重複と判断し、true を返す
        if (map.containsKey(num)) return true;
        
        // 初めての要素であればマップに登録する
        map.put(num, true);
    }

    // 最後まで重複が見つからなければ false を返す
    return false;
}

このコードの良い点

  • for文が1回で済む(O(n))
    配列 nums を左から右へ一度だけリニアスキャンしており、ネストループがないため処理が高速。

  • HashMapを活用して探索を高速化
    HashMap のキーアクセスは定数時間 O(1) で行える。
    そのため、要素の存在確認を非常に効率的に行える。

リニアスキャンとは、配列要素をひとつずつ順にアクセスする操作を指す。


ロジックの詳細

  1. 配列 nums を左から右へ順に走査する。

  2. 各要素 num について、map にそのキーがすでに存在するか確認する。

    • 存在していれば重複あり → true を返す。
    • 存在していなければ map.put(num, true) で登録する。
  3. 最後まで重複が見つからなければ false を返す。

途中で重複を検出した時点でループを終了できるため、無駄な計算を行わない点も優れている。


HashMapのアクセスがO(1)となる理由

HashMap は内部的にハッシュ関数を利用しており、キーから格納位置(インデックス)を直接計算する。
そのため、配列のように線形探索する必要がなく、平均的には定数時間でアクセスできる。

このように、

  • 要素の存在確認 (containsKey)
  • 追加 (put)

の両方が O(1) で完結する。

この「定数時間アクセス」の考え方は、多くのアルゴリズム最適化に応用できる重要な基礎である。

参考:


まとめ

  • ネストループによる O(n²) の実装は避けよう(計算量を意識しよう)。
  • 1回のリニアスキャンとHashMapを利用すれば メソッドとしてO(n) での実装が可能。
  • ハッシュアクセスのO(1)という考え方は、他の問題にも応用できる。

個人的に思うこと

AI支援によるコード生成(バイブコーディング)を行う際でも、
このような基本的なアルゴリズムの定石や最適化パターンを理解しておくことは重要だと感じる。

まぁAIに「最適化して」と指示すればある程度は改善されるが、
パフォーマンスや責任が伴う箇所では、自分自身で判断できる知識が必要になる。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?