概要(「何をしたいか」から逆算してデータ構造を選ぶ)
データ構造は「どんな操作を、どれくらいの規模で、どんな制約の下で行うか」で選びます。最重要ポイントは、必要な操作(検索・追加・削除・並べ替え・集計)、順序や重複の扱い、キーでのアクセスの必要性、変更可否(ミュータブルか)、性能とメモリのバランス。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)
Pythondeque(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問に圧縮して答え、得意な構造に任せる。作らず流す・先頭操作を避ける・キー計算を前取りするなどの勘所を添えれば、短く、速く、壊れない選択ができます。
