1. nums=[2,7,11,15], target=9 -> [0,1]
  2. nums=[3,2,4], target=6 -> [1,2]
  3. nums=[3,3], target=6 -> [0,1]
  4. nums=[1,5,8,3,4], target=7 -> [3,4]
  5. nums=[-1,-2,-3,-4,-5], target=-8 -> [2,4]
  6. nums=[0,4,3,0], target=0 -> [0,3]
コース一覧
コンピューターサイエンス上級:アルゴリズムとデータ構造
two sum (hash で O(n))

コンピューターサイエンス上級:アルゴリズムとデータ構造

探索、ソート、木、グラフ、動的計画法、貪欲法など、競技プログラミングや技術面接で問われる高度なアルゴリズムとデータ構造を学べるコースです。基本的な CS の知識を持ち、アルゴリズム力を伸ばしたい学習者や、外資・大手の技術面接対策をしたい方を対象としています。約 13 時間 (1 日 30 分 × 26 日) で 50 レッスンを修了でき、修了後はコーディング面接の実装問題を構造的に解けるようになります。

1
連結リスト
01. リンクリスト構築と長さの計算5分
02. リンクリストの反転5分
03. リンクリストのサイクル検出5分
04. ソート済みリンクリストの merge5分
05. リンクリストの中央ノード取得5分
06. ソート済みリストの重複削除5分
07. 第1章まとめクイズ5分
2
二分木
01. 二分木の in-order 走査5分
02. 二分木の pre-order 走査5分
03. 二分木の post-order 走査5分
04. 二分木の幅優先走査 (BFS)5分
05. 二分木の高さ5分
06. 二分木の平衡判定5分
07. 第2章まとめクイズ — 二分木5分
3
探索木 (BST)
01. BST に値を挿入する5分
02. BST から値を検索する5分
03. BST の最小値と最大値5分
04. BST 妥当性チェック5分
05. BST で k 番目に小さい値5分
06. BST から値を削除する5分
07. 第 3 章 まとめクイズ5分
4
ハッシュとセット
01. hashmap で頻度集計5分
02. キーでグループ化5分
03. two sum (hash で O(n))5分
04. 部分配列の和 = k の個数5分
05. 最長連続部分列5分
06. 集合の積 (intersection)5分
07. 第4章まとめクイズ5分
5
グラフ
01. グラフ BFS で連結成分サイズを求める5分
02. グラフ DFS で連結成分の数を数える5分
03. グラフのパス存在判定5分
04. BFS で最短経路の長さを求める5分
05. トポロジカルソート5分
06. 2 部グラフ判定5分
07. ダイクストラ法 — 重み付きグラフの最短経路15分
08. 第5章まとめクイズ — グラフ5分
6
動的計画法 (上級)
01. 編集距離 (レーベンシュタイン距離)5分
02. 最長増加部分列(LIS)の解法 ── AOJ 2430 対応の動的計画法5分
03. 最大部分配列和 (Kadane)5分
04. 隣り合わない最大値 (House Robber)5分
05. グリッド経路数 (Unique Paths)5分
06. 単語分割可能か (Word Break)5分
07. 第6章まとめクイズ — 動的計画法 (上級)5分
7
総合データ構造と関数型
01. map と filter を組み合わせる5分
02. reduce で積を計算5分
03. 関数合成5分
04. カリー化5分
05. trie の単純検索 (prefix マッチ)5分
06. Union-Find (連結成分数)5分
07. 最終総まとめクイズ5分

two sum (hash で O(n))

相手を探すたびに、配列を歩き直している

和が target になる 2 つの位置を探す問題です。素直に書けば、1 つ目を決めて、その右側を全部見て相手を探し、見つからなければ 1 つ目をずらす。この形で何回比べることになるか、数だけ数えてみます。

JavaScript

let compares = 0; for (let i = 0; i < nums.length; i++) { for (let j = i + 1; j < nums.length; j++) compares += 1; } // 1000 個で 499500 回、10 万個なら約 50 億回

原因ははっきりしています。i を 1 つ進めるたびに、右側をまた最初から見ているからです。

欲しいのは値そのものではなく「もう出たかどうか」

見方を変えます。今 nums[i] を見ているとき、組める相手はもう決まっています。target - nums[i] です。この値を右側から探しに行くのではなく、「左側にもう出ていたか」を聞くことにします。

聞く相手が配列なら、結局は端から歩くので何も変わりません。そこで、通り過ぎた値を控えておく場所を用意します。

JavaScript

const seen = new Map(); seen.set(7, 1); // 値 7 は 1 番目に出た、と控える seen.has(7); // true — 控えが何件に増えても 1 回で返る

配列の includes は中身が増えるほど遅くなりますが、Map の has は増えても変わりません。この差が、二重ループを 1 周に変えます。控える中身を「出たかどうか」ではなく「何番目に出たか」にしておくのは、返すのが値ではなく位置だからです。

控えるのは、探した後

順番を間違えると、静かに壊れます。nums = [3, 3] で target = 6 のときを追ってみてください。

先に自分を控えてから相手を探すと、i が 0 の時点で「3 はもう出ている」と自分自身を見つけます。返るのは同じ位置を 2 回使った答えで、これは誤りです。テストによっては形が合っているぶん、見つけるのに時間がかかります。

nums = [2, 7, 11, 15] で target = 9 を追うと、2 の番では欲しい値が 7 ですが控えはまだ空なので、2 を控えて次へ進みます。7 の番では欲しい値が 2 で、これはもう控えにあります。2 歩で終わりました。残りの 11 と 15 は一度も見ていません。

各歩で「まず探す、無ければ控える」の順に固定してください。こうすれば、控えに入っているのは必ず自分より左の要素だけになります。同じ値が何度も出てくる配列でも、控えが上書きされるだけで筋は通ります。

返すのは値ではなく位置

最後に仕様の確認です。返すのは [3, 7] のような値ではなく、[0, 1] のような位置です。しかも昇順に並べます。控えから出てくるほうが必ず左にいるので自然と昇順になりますが、組み立て方によっては入れ替わります。返す直前に並べておくと確実です。

答えは必ず 1 組あるという前提なので、見つからなかった場合を作り込む必要はありません。

要件

  1. 二重ループ O(n^2) ではなく hashmap を使った O(n) で解く
  2. 戻り値は昇順にソートしたインデックスペア (例: [0, 1])
  3. 同じインデックスを 2 回使ってはいけない (i != j)

入出力例

twoSum([2,7,11,15], 9) → [0,1] twoSum([3,2,4], 6) → [1,2] twoSum([3,3], 6) → [0,1] twoSum([1,5,8,3,4], 7) → [3,4] twoSum([-1,-2,-3,-4,-5], -8) → [2,4] twoSum([0,4,3,0], 0) → [0,3]

ヒント

`seen` (値 -> インデックス) のマップを作り、各要素について `target - v` が `seen` にあるか調べる

「探してから登録」の順序を守る。先に登録すると `nums = [3, 3], target = 6` のようなケースで同一インデックスのバグが入る

見つかった `[seen[need], i]` を返す前に昇順ソートを忘れない

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/05/19·更新 2026/08/26

関連レッスン

  • 部分配列の和 = k の個数

    整数配列と `k` を受け取り、和が `k` になる **連続する部分配列** の個数を `hashmap` と累積和で `O(n)` で数える。

  • 最長連続部分列

    整数配列を `set` に入れて、連続する整数で最も長いシーケンスの長さを `O(n)` で求める。

  • 集合の積 (intersection)

    2 つの整数配列の共通要素 (集合の積) を `set` で求めて、重複なしの昇順リストで返す。

  • 第4章まとめクイズ

    ハッシュとセットの計算量・性質・使いどころを 4 択クイズで総点検する。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • 配列サイズ固定の同型データの集まり
  • ループ繰り返し処理。矢印で戻すか専用記号で示す
  • テストバグを見つける工程
  • 戻り値呼び出し元への返答を表す点線矢印
  • インデックスDB 検索を速くする索引
main.py
学習モード
エディタを読み込んでいます

メモ

two sum (hash で O(n))

⌘S で保存