ウェブエンジニア問題集
第2章

計算量の考え方 — 「制限時間2秒」で何回ループを回せるか

6
この章の目次開く

コーディングテストには「実行時間制限」があります。paizaもAtCoderも、だいたい1〜2秒です。正しい答えを出すコードでも、遅ければTLE(Time Limit Exceeded)で不正解になります。

この章で身につけるのは、コードを書く前に「この解き方で間に合うか」を判断する力です。これがないと、時間をかけて実装した解法が無駄になります。逆にこれがあると、書くべき解法が最初から絞れます。

計算量とBig-O記法 — 30秒で復習

計算量は「入力サイズNが増えたとき、処理回数がどう増えるか」の分類で、**O(記法)**で表します。

記法増え方典型例
O(1)一定配列の添字アクセス、ハッシュマップの参照
O(log N)ゆっくり二分探索
O(N)比例1重ループ
O(N log N)ほぼ比例高速なソート
O(N²)2乗2重ループ
O(2^N)爆発部分集合の全列挙

理論面の丁寧な説明は『コンピュータサイエンスの基礎』第3章 アルゴリズムと計算量にあります。本章はこれを「テストで使う」ことに集中します。

実戦の核心: 10^8回ルール

見積もりに使う経験則はこれだけです。

1秒間に処理できるのは、単純な演算でおよそ10^8(1億)回。

制限時間2秒なら、余裕を見て10^8回以内に収まる解法を選びます。これを使うと、問題文の制約(Nの最大値)から、許される計算量が逆算できます。

Nの最大値間に合う計算量選ぶべき解法の例
N ≤ 10O(N!) や O(2^N)順列全列挙もOK
N ≤ 20O(2^N)bit全探索(第7章)
N ≤ 3,000O(N²)2重ループOK
N ≤ 10^5O(N log N)ソート・二分探索・優先度付きキュー
N ≤ 10^7O(N)1重ループのみ
N > 10^8O(log N) や O(1)数式で解く・二分探索
学習者学習者

えっ、これ逆に言うと「N ≤ 10^5 なら2重ループは絶対アウト」って、問題を読んだ瞬間に分かっちゃうってこと?

その通りです。10^5の2乗は10^10、つまり100秒コース。制約は出題者からのヒントなんです。「N ≤ 10^5」と書いてあったら、出題者は「O(N²)では解かせません。O(N log N)以下の解法がありますよ」と言っています。問題文を読んだら、本文より先に制約を見る癖をつけましょう。

実例: 同じ問題を3つの計算量で解く

「配列の中から、合計がXになる2つの要素があるか判定せよ」という定番問題で、計算量の違いを体感します。

解法1: 2重ループ — O(N²)

function hasPairSum(nums: number[], x: number): boolean {
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] + nums[j] === x) return true;
    }
  }
  return false;
}
typescript

素直ですが、N = 10^5 だと約5×10^9回の比較でTLEです。

解法2: ソート + 二分探索 — O(N log N)

各要素 a について「x - a が存在するか」を二分探索で調べます。探索1回がO(log N)、それをN回なのでO(N log N)。N = 10^5 なら約1.7×10^6回、余裕で間に合います。

解法3: ハッシュセット — O(N)

function hasPairSum(nums: number[], x: number): boolean {
  const seen = new Set<number>();
  for (const a of nums) {
    if (seen.has(x - a)) return true;  // 探索はO(1)
    seen.add(a);
  }
  return false;
}
typescript

「見たことのある値」をSetに入れながら1周するだけ。同じ問題でも、データの持ち方を変えるだけで10^10回が10^5回になる——これが本書全体を貫くテーマです。

段階的に改善するイメージ

計算量を落とす3つの定番ルート

解法2・3で使った「削り方」は、実は数パターンしかありません。本書で繰り返し登場する3ルートを予告しておきます。

ルート何をO(1)/O(log N)にするか学ぶ章
ハッシュマップ/Set「存在するか?」の探索第4章
ソート + 二分探索「条件を満たす境界」の発見第8章
累積和・尺取り法「区間の合計」の計算第3章

「2重ループを書きたくなったら、この3つのどれかで消せないか考える」——それだけで解ける問題が一気に増えます。

見落としがちな計算量の罠

TypeScriptでコーディングテストを受けるとき特有の注意点があります。**一見O(1)に見えて実はO(N)**な操作です。

arr.includes(v)     // O(N)! Setのhas()ならO(1)
arr.indexOf(v)      // O(N)!
arr.shift()         // O(N)! 先頭削除は全要素がずれる
str + appendText    // ループ内での文字列連結は要注意(配列にpushしてjoinが安全)
typescript

ループの中でこれらを呼ぶと、気づかぬうちにO(N²)になります。「ループ内で配列を線形に触っていないか」はTLEの定番チェックポイントです。

メンターメンター

提出前のセルフチェックは2つだけ。「制約のNは最大いくつか」「自分のコードは何乗のループか」。この掛け算が10^8を超えていたら、書き直しです。慣れれば10秒でできる確認ですよ。

まとめ

  • 実行時間制限は1〜2秒。1秒 ≒ 単純演算10^8回が見積もりの基準
  • 制約は出題者のヒント。N ≤ 10^5 なら O(N log N) 以下、N ≤ 20 なら O(2^N) もOK——と逆算する
  • 計算量を落とす定番ルートは「ハッシュ化」「ソート+二分探索」「累積和・尺取り法」の3つ
  • includes / shift などの隠れO(N)操作をループに入れない
  • 問題を読む順番は「制約 → 本文」。書く前に計算量、が鉄則

次章からPart 2・データ構造編です。まずは全ての基本、配列と文字列の頻出パターンから始めます。