Python | データ構造強化:データ構造の選択基準

Python Python
スポンサーリンク
  1. 概要(「何をしたいか」から逆算してデータ構造を選ぶ)
  2. 基本方針(ここが重要)
    1. まず「操作」を列挙し、優先順をつける
    2. 順序・重複・キーの要件を明文化する
  3. 構造別の選択基準と使いどころ
    1. リスト(list):順序・インデックス・柔軟操作が欲しい
    2. タプル(tuple):不変・安全・ハッシュ可能な“レコード”
    3. 辞書(dict):キーで即引き(検索が多いなら最有力)
    4. 集合(set):重複禁止・高速会員判定・集合演算
    5. deque(collections.deque):両端 O(1)(キュー・双方向の追加削除)
    6. 優先度キュー(heapq):最小(最大)優先の取り出し
    7. キュー(queue.*):並行処理の安全受け渡し
    8. 記録用の軽量“レコード”(namedtuple / dataclass)
    9. カウンタ・欠損初期化(Counter / defaultdict)
  4. 性能とメモリの勘所(間違いやすい重要ポイント)
    1. 先頭操作が多いなら list は避ける(dequeへ)
    2. 会員判定は「set/dict の in」一択
    3. ソートの前処理(key を軽く、型を正しく)
    4. メモリは“作らず流す”(ジェネレータ式)
  5. 選択のための小さな設計チェックリスト(短く確実に決める)
    1. 要件を3問に圧縮して答える
  6. 例題で身につける(定番から一歩先まで)
    1. 例題1:IDで即引き+順序表示(辞書+リスト)
    2. 例題2:重複禁止のタグ+表示整形(set→sorted)
    3. 例題3:先頭から処理するタスク列(deque)
    4. 例題4:頻度トップN(Counter+heapq)
    5. 例題5:カテゴリ別のグルーピング(defaultdict)
  7. まとめ

概要(「何をしたいか」から逆算してデータ構造を選ぶ)

データ構造は「どんな操作を、どれくらいの規模で、どんな制約の下で行うか」で選びます。最重要ポイントは、必要な操作(検索・追加・削除・並べ替え・集計)、順序や重複の扱い、キーでのアクセスの必要性、変更可否(ミュータブルか)、性能とメモリのバランス。Pythonの組み込みは用途別に最適解が用意されているので、「要件→構造→実装」の順に決める癖をつけましょう。

基本方針(ここが重要)

まず「操作」を列挙し、優先順をつける

  • 例:IDで即座に引ける必要がある(キー検索が多い)→ 辞書(dict)
  • 例:順序付きで並べる・取り出す(並べ替えやインデックスアクセスが多い)→ リスト(list)
  • 例:重複禁止で“集合演算”(和・差・積)を多用→ 集合(set)
  • 例:末尾の追加・削除が中心、順序保持で軽量→ リスト(list)または deque
  • 例:先頭から取り出す“キュー”が中心→ deque(単スレッド)/queue.Queue(並行)
  • 例:最小値・最大値を頻繁に取り出す→ heapq(優先度キュー)

操作の回数・規模に応じて「どれがボトルネックか」を見極め、得意な構造を選びます。

順序・重複・キーの要件を明文化する

  • 順序が必要か(挿入順・ソート順)/いらないか
  • 重複を許すか/禁止か
  • 位置でアクセスするか(インデックス)/キーでアクセスするか(辞書)

ここを先に決めると、構造の選択が一気に楽になります。

構造別の選択基準と使いどころ

リスト(list):順序・インデックス・柔軟操作が欲しい

  • 強み:末尾の append/pop は高速。スライス、ソート、インデックスアクセスが便利。
  • 弱み:先頭の挿入・削除は遅い(O(n))。
  • 使いどころ:順序付きのコレクション、並べ替え、ページング、スタック(末尾)など。
xs = []
xs.append(10)
xs[-1]          # 末尾
sorted(xs)      # 新しい並べ替え結果
Python

タプル(tuple):不変・安全・ハッシュ可能な“レコード”

  • 強み:変更不可で安全、辞書のキーや集合要素に使える。軽量。
  • 弱み:後から直せない。
  • 使いどころ:座標、設定値の束、辞書キー、関数の複数戻り値。
point = (x, y)
cache = {("user", 101): "alice"}
Python

辞書(dict):キーで即引き(検索が多いなら最有力)

  • 強み:平均 O(1) のキー検索・更新。柔軟な値(任意型)。
  • 弱み:順序は“挿入順”だがソートではない(必要なら別途 sorted)。
  • 使いどころ:ID→オブジェクト、設定、集計(キー別カウント)、インデックス作成。
users = {"id:101": {"name": "alice"}}
users["id:101"]["name"]
Python

集合(set):重複禁止・高速会員判定・集合演算

  • 強み:in 判定が高速、和・差・積集合が一行で書ける。
  • 弱み:順序なし(表示時の並びは不定)。
  • 使いどころ:重複排除、OR/AND/NOT 条件の集合処理、既出チェック。
seen = set()
if item not in seen:
    seen.add(item)
Python

deque(collections.deque):両端 O(1)(キュー・双方向の追加削除)

  • 強み:append/pop とともに popleft/appendleft が O(1)。
  • 弱み:任意位置のアクセスは不得手。
  • 使いどころ:FIFO キュー、ログバッファ、スライド窓。
from collections import deque
q = deque()
q.append("A")
q.popleft()
Python

優先度キュー(heapq):最小(最大)優先の取り出し

  • 強み:最小値の取り出しが O(log n)。多量の「小さい順の処理」に強い。
  • 弱み:中間要素の削除・更新が面倒。
  • 使いどころ:スケジューラ、トップN、Dijkstra など。
import heapq
h = []
heapq.heappush(h, (priority, item))
prio, val = heapq.heappop(h)
Python

キュー(queue.*):並行処理の安全受け渡し

  • 強み:スレッドセーフ、ブロッキング・タイムアウトを制御。
  • 弱み:単純用途ではオーバーヘッド。
  • 使いどころ:プロデューサ/コンシューマ、バックプレッシャー制御。
import queue
q = queue.Queue()
q.put(job)
job = q.get()
Python

記録用の軽量“レコード”(namedtuple / dataclass)

  • 強み:フィールド名で扱えるレコード。読みやすく、型管理がしやすい。
  • 使いどころ:行データ、イベント、設定の構造化。
from dataclasses import dataclass
@dataclass
class User: id: int; name: str
Python

カウンタ・欠損初期化(Counter / defaultdict)

  • Counter:頻度集計の最短手段。
  • defaultdict(list/set/int):欠損時の初期値を自動付与。
from collections import Counter, defaultdict
freq = Counter(words)
groups = defaultdict(list)
groups[key].append(val)
Python

性能とメモリの勘所(間違いやすい重要ポイント)

先頭操作が多いなら list は避ける(dequeへ)

list.pop(0) や insert(0, x) は O(n) で急速に重くなります。先頭を触るなら deque を選びます。

会員判定は「set/dict の in」一択

「存在チェック」は list ではなく、set/dict を使うと桁違いに速く、コードも短いです。

ソートの前処理(key を軽く、型を正しく)

並べ替えが多いなら、sorted の key で比較用の値に変換してからソート。日付文字列は datetime へ、バージョンは分割してタプルへ。

メモリは“作らず流す”(ジェネレータ式)

巨大データはリストを作らず、sum や any/all へジェネレータを渡して遅延処理。「作る必要がある場面だけ」作るのが原則。

選択のための小さな設計チェックリスト(短く確実に決める)

要件を3問に圧縮して答える

  • 「主に何をする?」検索/並べ替え/追加削除/集計/順序制御
  • 「順序・重複・キーの要件は?」挿入順か/重複禁止か/キーで引くか
  • 「並行・リアルタイム性は?」スレッドセーフか/先頭取り出しか/優先度制御か

この回答から「最初の構造」を決め、必要なら補助構造(例:辞書+リスト、set+list)を併用します。

例題で身につける(定番から一歩先まで)

例題1:IDで即引き+順序表示(辞書+リスト)

users = {101: {"name": "alice"}, 102: {"name": "bob"}}
order = [102, 101]  # 表示順
for uid in order:
    print(uid, users[uid]["name"])
Python

例題2:重複禁止のタグ+表示整形(set→sorted)

tags = set()
for t in ["coffee", "tea", "coffee", "sugar"]:
    tags.add(t)
print(sorted(tags))  # ['coffee', 'sugar', 'tea']
Python

例題3:先頭から処理するタスク列(deque)

from collections import deque
tasks = deque(["T1", "T2", "T3"])
while tasks:
    t = tasks.popleft()
    print("do:", t)
Python

例題4:頻度トップN(Counter+heapq)

from collections import Counter
import heapq

freq = Counter(["a","b","a","c","a","b"])
top = heapq.nlargest(2, freq.items(), key=lambda kv: kv[1])
print(top)  # [('a', 3), ('b', 2)]
Python

例題5:カテゴリ別のグルーピング(defaultdict)

from collections import defaultdict
by_cat = defaultdict(list)
for cat, name in [("coffee","latte"), ("tea","earl"), ("coffee","mocha")]:
    by_cat[cat].append(name)
print(by_cat)  # 自動初期化で安全
Python

まとめ

データ構造の選択は「操作」「要件(順序・重複・キー)」「性能と並行性」を明文化することから始まります。順序とインデックスならリスト、キー検索なら辞書、重複禁止と会員判定なら集合、両端操作なら deque、優先度付き取り出しなら heapq、並行処理なら queue。頻度集計や欠損初期化には Counter / defaultdict。まずは要件を3問に圧縮して答え、得意な構造に任せる。作らず流す・先頭操作を避ける・キー計算を前取りするなどの勘所を添えれば、短く、速く、壊れない選択ができます。

タイトルとURLをコピーしました