リリース・改善中
Algorithm
配列・連結リスト・スタック・キュー・ハッシュテーブルの仕組みと各操作の計算量を学び、場面に合った構造を選べるようにします。
データ構造とは、データをメモリ上にどう並べるかと、その並べ方で効率よく行える操作をひとまとめにしたものです。このトピックでは、ほとんどのプログラムが使う五つの基本構造、つまり配列と動的配列、連結リスト、スタック、キューと両端キュー(deque)、ハッシュテーブルを扱います。連続したメモリとポインタでつないだメモリの違い、ハッシュ関数、衝突、負荷率もあわせて説明します。
どのデータ構造を選ぶかで、同じ処理が O(1) にも O(n) にもなります。リストの先頭から取り出してキューを作ったり、リストで所属判定をしたりすると、データが増えたときにプログラムが急激に遅くなります。Python の list・deque・dict・set、C++ の vector・deque・unordered_map、Java の ArrayList・ArrayDeque・HashMap、TypeScript の Array・Map・Set が実際にどんな構造なのかを知っておけば、こうした落とし穴を避けられます。コーディング面接でも最初に確認される内容です。
まず小さな例で各構造を手でたどり、メモリ上で何が動くのかを確かめます。次にスタック、リングバッファによるキュー、チェイン法のハッシュマップを実装し、操作ごとの計算量を表にまとめてタイマーで実測します。最後に、どの構造を選ぶかが鍵となる練習問題に取り組みます。
配列はインデックスによる O(1) アクセスとキャッシュ効率の良さを、連結リストは手元のノードの隣での O(1) 挿入・削除をもたらします。
動的配列は一定の倍率で容量を増やして要素をコピーするため、ときどき高価な追加があっても平均すると O(1) です。
スタックは最も新しい要素を、キューは最も古い要素を取り出します。deque やリングバッファを使えば両端の操作がすべて O(1) になります。
ハッシュテーブルはキーをバケットに対応させて平均 O(1) で検索します。衝突はチェイン法などで処理し、負荷率が上限を超えたら拡張します。
チェイン法によるハッシュマップです。キーのハッシュ値をバケット数で割った余りでバケットを選び、既存のキーなら値だけを置き換え、負荷率が 0.75 を超えるとバケットを倍にしてすべてのペアを入れ直します。あわせて、リストをスタックとして、deque をキューとして使う例も示します。
data_structures.py
from collections import deque
class HashMap:
"""Separate chaining: each bucket is a list of (key, value) pairs."""
def __init__(self, capacity=8):
self.buckets = [[] for _ in range(capacity)]
self.size = 0
def _bucket(self, key):
return self.buckets[hash(key) % len(self.buckets)]
def put(self, key, value):
bucket = self._bucket(key)
for i, (k, _) in enumerate(bucket):
if k == key:
bucket[i] = (key, value)
return
bucket.append((key, value))
self.size += 1
if self.size > 0.75 * len(self.buckets): # load factor limit
old = self.buckets
self.buckets = [[] for _ in range(2 * len(old))]
for pairs in old:
for k, v in pairs:
self._bucket(k).append((k, v))
def get(self, key, default=None):
for k, v in self._bucket(key):
if k == key:
return v
return default
stack = [1, 2, 3]
stack.append(4)
print("stack pop:", stack.pop()) # 4 (LIFO)
queue = deque([1, 2, 3])
queue.append(4)
print("queue popleft:", queue.popleft()) # 1 (FIFO)
ages = HashMap()
for name, age in [("ada", 36), ("alan", 41), ("grace", 85)]:
ages.put(name, age)
ages.put("ada", 37)
print("ada:", ages.get("ada"), "size:", ages.size) # ada: 37 size: 3
インストールから 基本データ構造 の中心となる考え方まで、6 章で順を追って学びます。
基本データ構造 について質問し、経験を共有し、意見を交わす場所です。
まだディスカッションはありません。最初の話題を始めましょう。
python data_structures.py
コメント 0件
ログイン · ログインするとコメントできます。
最初のコメントを書いてみましょう。