Pythonで解く「Two Sum(2つの合計)」問題:ハッシュマップを使った効率的な実装方法
整数の配列が与えられたとき、その中から2つの要素を選び、その合計が指定されたターゲット値と一致するようなインデックスのペアを返す問題を考えてみましょう。これは、アルゴリズム学習やコーディング面接で非常によく知られている「Two Sum(2つの合計)」問題です。
ここでは、配列内に必ず一意な解が1つだけ存在するという前提を置きます。つまり、同じターゲットに対して複数のインデックスの組み合わせが存在することはありません。
問題の例
例として、配列 A = [2, 8, 12, 15]、ターゲットの合計値が 20 である場合を考えます。このとき A[1] + A[2] = 8 + 12 = 20 となるため、答えはインデックス 1 と 2 を返します。
解法のアプローチ:ハッシュマップ(辞書)を活用
最も単純な方法は全てのペアを総当たりで調べることですが、計算量が O(n²) となり非効率です。そこで、ハッシュマップ(Pythonでは辞書)を使うことで、時間計算量 O(n) で解くことができます。
基本的な考え方は次の通りです。配列を先頭から順に走査しながら、「現在の要素と足し合わせてターゲットになる値(補数)」がすでに登場済みかどうかを辞書で確認します。
具体的な手順
- 結果を保持するための空の辞書(マップ)
resを用意する - インデックス i を 0 から n - 1 まで順にループする(n は配列の要素数)
- もし
target - A[i](補数)がすでにresに存在すれば、その補数のインデックスと現在の i を答えとして返す - 存在しなければ、
res[A[i]] = iとして現在の値とインデックスを記録する
- もし
この方法なら、各要素を一度ずつ調べるだけで済むため、非常に効率的です。
Pythonでの実装例
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
required = {}
for i in range(len(nums)):
if target - nums[i] in required:
return [required[target - nums[i]], i]
else:
required[nums[i]] = i
input_list = [2, 8, 12, 15]
ob1 = Solution()
print(ob1.twoSum(input_list, 20))入力
input_list = [2, 8, 12, 15] target = 20
出力
[1, 2]
まとめ
この実装では、辞書 required に「これまでに見た値 → インデックス」を記録していきます。各要素について、ターゲットとの差分(補数)がすでに記録済みかどうかを O(1) で確認できるため、全体の処理は線形時間で完了します。
空間計算量は O(n)、時間計算量は O(n) となり、総当たり方式(O(n²))と比べて大幅に効率化できるのがポイントです。「Two Sum」はハッシュマップ活用の基本形となる問題なので、ぜひこのパターンを覚えておきましょう。
-
Pythonで2つの整数の合計を求める方法|+と-を使わないビット演算テクニック
問題概要 2つの整数 a と b が与えられたとき、その合計を求めることを考えます。ただし、+ や - のような算術演算子は使用できません。例えば、a = 5、b = 7 の場合、答えは 12 になります。 解決のアプローチ:ビット演算を活用する この問題は、ビット単位の論理演算子を組み合わせることで解決できます。ポイントは次の3つです。 XOR(^:排他的論理和) … 桁上がりを考慮しない「部分和」を計算します。 AND(&:論理積) … 桁上がりが発生する位置を検出します。 左シフト(<< 1) … 検出した桁上がりを1つ上の位へ移動させます。 アルゴリズムの手
-
Pythonで二分木のパス合計(Path Sum)を判定する方法
パス合計問題とは二分木と目標の合計値が与えられたとき、根から葉までの経路をたどった際のノード値の合計が、与えられた値と一致するような経路が存在するかどうかを判定します。例として、木が [0, -3, 9, -10, null, 5] という構成で、合計値が 14 の場合を考えてみましょう。このとき、0 → 9 → 5 という経路が存在し、その合計はちょうど 14 になるため、答えは True となります。解法のアプローチこの問題は再帰を使うことで簡潔に解くことができます。手順は以下の通りです。根ノードが null(空)の場合、False を返します。左右の子ノードが両方とも空(つまり葉ノード)