hashmap で頻度集計

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

hashmap で頻度集計

このレッスンで分かること

  • 配列に同じ値が何回現れるかを知りたい場面は、データ分析の現場でも実装の現場でも頻繁にあります
  • 素朴に「ある値が何回出るかを数える」ことを for の二重ループで書くと O(n^2) になってしまいます
  • この関数 mostFrequent(arr) は整数の配列を受け取り、最も多く現れた値 を 1 つ返します

hashmap で頻度集計 とは

配列の各要素が何回現れたかを hashmap で集計し、最も多く出た値を返す関数を実装する。本レッスンでは、hashmap で頻度集計 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

配列に同じ値が何回現れるかを知りたい場面は、データ分析の現場でも実装の現場でも頻繁にあります。アンケートの回答集計、ログのエラーコード集計、テキスト中の単語出現回数など、対象が変わっても 頻度を数える という操作は本質的に同じです。本レッスンでは hashmap (Python の dict、JavaScript の Map / Object、Java の HashMap、Go の map) を使って、配列の頻度集計を O(n) で実装します。

素朴に「ある値が何回出るかを数える」ことを for の二重ループで書くと O(n^2) になってしまいます。1 要素ごとに、配列全体を走査して一致を数えるからです。これを hashmap で 1 パスに置き換えるのが今回のテーマです。

配列の頻度集計は hashmap の代表例。O(n^2) から O(n) に落とすための基本パターンを身につけよう。

仕様

この関数 mostFrequent(arr) は整数の配列を受け取り、最も多く現れた値 を 1 つ返します。同点が複数ある場合は、最も小さい値 を返すことにします。配列が空のときは -1 を返します。

アルゴリズム

手順は次の通りです。

  1. hashmap を 1 つ用意する (キーは値、バリューは出現回数)
  2. 配列を 1 度だけ走査して、map[v] = map[v] + 1 を行う
  3. map を走査して、最大の頻度を持つ値の中で 最小の値 を選ぶ

各ステップは O(n) なので、合計でも O(n) で済みます。素朴な二重ループの O(n^2) と比べて、要素が 1 万個あれば 1 万倍速くなる計算です。

Mermaid で流れを確認

diagram (will load when visible)

Python での実装

Python

def mostFrequent(arr): if not arr: return -1 freq = {} for v in arr: freq[v] = freq.get(v, 0) + 1 best_val = None best_count = -1 for v, c in freq.items(): if c > best_count or (c == best_count and v < best_val): best_val = v best_count = c return best_val

freq.get(v, 0) は「キーがなければ 0 を返す」という安全なアクセスです。freq[v] だけだと KeyError になります。

JavaScript での実装

JavaScript

function mostFrequent(arr) { if (arr.length === 0) return -1; const freq = new Map(); for (const v of arr) { freq.set(v, (freq.get(v) || 0) + 1); } let bestVal = null; let bestCount = -1; for (const [v, c] of freq) { if (c > bestCount || (c === bestCount && v < bestVal)) { bestVal = v; bestCount = c; } } return bestVal; }

MapObject と違い、キーの型を保ったまま保存できます。整数キーをそのまま使えるので、競技プログラミング系の問題と相性が良いです。

よくある間違い

1 つ目は freq[v] = freq[v] + 1 のように、初回アクセス時の undefined を考慮しないケースです。Python なら KeyError、JS なら NaN になります。get(v, 0)|| 0 で初期値を補いましょう。

2 つ目はタイブレークの条件です。同点のときに何を返すか仕様で決めないと、テストが揺れます。本問では「最も小さい値」と決めています。実装時は c == best_count and v < best_val両方 をチェックしてください。

3 つ目は空配列です。何も入っていない配列に対して「最頻値」は定義できないので、-1 を返す約束にしています。

集合や辞書の問題では「同点 / 空 / 単一要素」の 3 つのエッジケースを必ず先に考える。

やってみよう

  • [1, 2, 2, 3, 3] を入力するとどう動くか手で追ってみる。最頻値は 23 で同点なので、より小さい 2 が返るはず。
  • 配列の代わりに 文字列 を渡したい場合に書き換えてみる。Python なら for ch in s: でそのまま動く。
  • 同じ集計を 二重ループ でも書いて、計算量の違いを体感する。

頻度集計は hashmap の超基本。これがスラスラ書けると group bytwo sum などの応用が一気に視野に入る。

よくある質問

Q. dict と Counter どちらを使えばよいですか?

A. Python の collections.Counter は頻度集計専用の dict サブクラスです。Counter(arr) の 1 行で集計が完了し、most_common(n) で上位 n 件も取れます。一方、自前で dict を使うと「初回は 0、以降はインクリメント」というロジックが明示されるので、学習目的や細かい条件分岐が必要な場面では dict のほうが見通しが良くなります。実務では Counter を積極的に使いましょう。

Q. キーが存在しないときの安全なアクセス方法は何ですか?

A. Python では freq[v] とすると存在しないキーで KeyError が発生します。freq.get(v, 0) を使うとキーがなければ 0 を返すので安全です。defaultdict(int) も選択肢で、初回アクセス時に自動で 0 をセットします。JavaScript の Map では freq.get(v) || 0 で同じことができます。

Q. 同点(タイブレーク)はどう扱えばよいですか?

A. 仕様次第で変わります。本レッスンでは「最も小さい値を返す」と定めました。実務では「最初に登場した値」「辞書順で先の値」など要件に合わせて決める必要があります。タイブレークが未定義のまま実装すると、入力順次第で結果が変わる非決定的な関数になってしまうので、必ず仕様に明記しましょう。

Q. JavaScript で Map と Object の使い分けは?

A. Object はキーが文字列(または Symbol)に変換されます。整数をキーにすると文字列化されるため、比較や計算に注意が必要です。Map はキーの型を保ったまま格納できるので、整数や配列をキーにしたい場合は Map が適しています。頻度集計で整数の値をキーとして使う本レッスンのようなケースでは Map を選ぶと型が保証されて安全です。

次のレッスン

次は キーでグループ化 で、配列の各要素が何回現れたかを hashmap で集計し、最も多く出た値を返す関数を実装する を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. 頻度集計 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. 頻度集計 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. 配列は最大数千要素まで対応すること
  2. 二重ループ (O(n^2)) ではなく hashmap を使った O(n) で実装する
  3. 頻度が同じ値が複数ある場合は、その中で最も小さい値を返す。空配列なら -1 を返す

入出力例

test-cases.txt

mostFrequent([1,2,2,3,3])2 mostFrequent([5,5,5,1,2])5 mostFrequent([7])7 mostFrequent([1,2,3])1 mostFrequent([4,4,3,3,2,2])2 mostFrequent([10,20,10,20,10])10

ヒント

main.py
main.py
学習モード

メモ

hashmap で頻度集計

⌘S で保存