コンテンツへスキップ
戻る

Leetcode 解法まとめ(1)

公開日:  at  08:00 午前

Two Sum は Leetcode を始める人が最初に解く定番の問題です。以前に解いたことはありましたが、自分の思考プロセスをきちんと言語化したくて、改めて記事にまとめました。では始めましょう。

Two Sum

問題の説明

配列 nums と整数 target が与えられます。足し合わせると target になる 2 つの要素のインデックスを返してください。解は必ず 1 つ存在することが保証されています。

解法

Solution 1

まず思いつくのはブルートフォースです。2 重ループで全ての組み合わせを試し、条件を満たした時点で即座に return します。Java の実装は以下のとおりです。

public int[] twoSum(int[] nums, int target) {
    for (int i = 0; i < nums.length; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            int ans = nums[i] + nums[j];
            if (ans == target) return new int[]{i, j};
        }
    }
    return new int[]{-1, -1};
}

問題は解けますが、時間計算量は O(n²) です。最悪の場合、約 n²/2 回の比較が発生します。空間計算量は O(1) で、追加のデータ構造は使用していません。

Solution 2

最適解は HashMap を使う方法で、時間計算量を O(n²) から O(n) に下げられます。考え方は、各要素の値とインデックスをマップに記録し、現在の要素を処理するたびに補完値(target - nums[i])がマップに存在するか確認するというものです。存在すれば、ペアが見つかったことになります。

public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();

    for (int i = 0; i < nums.length; i++) {
        if (!map.containsKey(target - nums[i])) {
            map.put(nums[i], i);
        } else {
            return new int[]{map.get(target - nums[i]), i};
        }
    }
    return new int[]{-1, -1};
}

時間計算量:O(n)(各要素を 1 回だけ走査)。空間計算量:O(n)(最悪の場合、n 件全てをマップに格納)。

まとめ

Two Sum はシンプルな問題ですが、いくつかの重要な概念を含んでいます。

だからこそ、Leetcode 最定番の入門問題とされているのです。

Two Sum の本質は「2 つの数を探す」のではなく、「すでに見た数を記憶し、その『もう片方』を探す」ことにあります。この考え方は多くの問題に応用できます。

Remove Duplicates from Sorted Array

問題の説明

昇順にソートされた配列が与えられます。配列をその場で操作し、先頭の k 個が重複のない有序な要素になるようにして、k を返してください。

解法

Solution 1

1 つのループで配列を走査しながら、重複カウンター duplicates(初期値 0)を管理します。現在の要素が前の要素と同じなら重複なのでカウンターを増やします。各要素をカウンター分だけ左にずらして書き込み、最終的な長さは nums.length - duplicates です。

public int removeDuplicates(int[] nums) {
    int duplicates = 0;
    for (int i = 1; i < nums.length; i++) {
        if (nums[i] == nums[i - 1]) {
            duplicates++;
        }
        nums[i - duplicates] = nums[i];
    }
    return nums.length - duplicates;
}

Solution 2

fast/slow の 2 ポインタを明示的に使う、より簡潔な書き方です。slow ポインタは新しい一意の要素が見つかった時だけ進みます。

public int removeDuplicates(int[] nums) {
    int slow = 1;
    for (int fast = 1; fast < nums.length; fast++) {
        if (nums[fast] != nums[fast - 1]) {
            nums[slow] = nums[fast];
            slow++;
        }
    }
    return slow;
}

まとめ

どちらも時間計算量 O(n)、空間計算量 O(1) です。

2 つの解法の核心は同じで、「次に書き込む有効な位置」をポインタで追跡するというものです。違いは Solution 1 が重複数をオフセットとして使うのに対し、Solution 2 は fast/slow を明示的に扱う点です。面試では Solution 2 の方が意図が伝わりやすく、こちらを優先することをお勧めします。

Best Time to Buy and Sell Stock

問題の説明

各要素が株価を表す配列が与えられます。1 回の売買で得られる最大利益を計算してください。

解法

利益を最大化するには、これまでの最小価格を記録しながら、現在の価格との差が最大になるタイミングを追跡します。

public int maxProfit(int[] prices) {
    int minPrice = Integer.MAX_VALUE;
    int maxProfit = 0;
    for (int price : prices) {
        minPrice = Math.min(minPrice, price);
        int profit = price - minPrice;
        maxProfit = Math.max(maxProfit, profit);
    }
    return maxProfit;
}

まとめ

時間計算量:O(n)。空間計算量:O(1)。

Best Time to Buy and Sell Stock の本質は、全ての買い売り組み合わせを列挙する必要がないことです。配列を 1 度走査しながら「現在の最安値」を維持するだけで、O(n) で最大利益を求められます。


修正を提案する
この記事をシェアする:

次の記事
Leetcode 解法メモ (2)