Skip to content

データ構造

はじめに

プログラム = データ構造 + アルゴリズム。 ここまでCPUが命令を実行する仕組みやOSがリソースを管理する方法を学びました。しかしプログラムが処理する中心的な対象はデータです——ユーザー情報、商品一覧、ソーシャルグラフ……これらのデータをメモリ上でどう整理するかが、プログラムの速度を直接左右します。「なぜあるプログラムは数万件のデータを高速に処理できるのに、別のプログラムは数百件で動かなくなるのか?」——その答えは多くの場合、データ構造の選択にあります。

この記事で学べること

この章を学び終えると、次の力が身につきます:

  • 直感的な判断力:要件を見ただけで、どのデータ構造を使うべきか頭に浮かぶ
  • パフォーマンス分析の視点:ボトルネックがデータ構造の選択ミスなのか、アルゴリズムの効率の問題なのかを判断できる
  • トレードオフ思考:「空間と時間のトレードオフ」を理解し、完璧なデータ構造は存在しないことを知る
  • コードリーディング力:HashMap、Stack、Queueといった用語に戸惑わなくなる
  • 後続学習の基礎:データベースインデックス、キャッシュシステム、検索エンジンなどの技術の土台を築く
内容コアコンセプト
第1章全体像4大データ構造、分類基準
第2章線形構造配列、連結リスト、スタック、キュー
第3章ハッシュテーブルハッシュ関数、衝突処理、O(1) 検索
第4章ツリー構造二分木、ファイルシステムツリー、DOMツリー
第5章グラフ構造有向グラフ、無向グラフ、探索アルゴリズム
第6章パフォーマンス比較時間計算量、空間計算量
第7章選定ガイドシナリオ分析、意思決定フロー

1. 全体像:データ構造とは何か?

本の整理方法を想像してみてください:

  • 床に積み上げる:目的の本を探すには一冊ずつ確認——これが最も原始的な保存方法
  • 番号順に本棚に並べる:直接その位置に行って取れる——これが配列
  • カテゴリごとに棚を分ける:まず棚を特定してから本を探す——これがハッシュテーブル
  • タイトル順に多段ラックに並べる:毎回半分ずつ除外——これがツリー

整理の仕方が違うだけで、本を探す効率は天と地ほどの差があります。データ構造とはデータの「整理方法」——データをどう保存し、どう検索し、どう変更するかを決めるものです。

Data Structure OverviewChoose a data organization model for each scenario
Data structures are like ways to organize a room: clothes in a wardrobe, books on shelves, and small items in drawers.
📚
Linear structures
Data arranged in order, like a row of books
ArrayLinked listStackQueue
🗂️
Hash structures
Fast lookup through a key
Hash tableDictionarySet
🌳
Tree structures
Hierarchy, like a family tree
Binary treeB-treeHeap
🕸️
Graph structures
Complex relationship networks
Directed graphUndirected graphNetwork graph
📚Linear structures
Features
One-to-one relationship between elements
A clear before-and-after order
Can use contiguous storage or linked storage
Use Cases
📝
Arrays: list data
Store ordered data such as student scores or product prices
🔄
Stacks: undo operations
Text editor undo history, last in first out
🎫
Queues: task scheduling
Print queues and task queues, first in first out
Operation Complexity
OperationAverage time
Access elementO(1)
Insert/deleteO(n)
Everyday Analogy
Like train cars connected in order
To find car 5, count directly to it; to insert a new car, you need to break and reconnect links.

すべてのデータ構造は4つの大きなカテゴリに分類できます:

種類データの関係代表例日常での例え
線形構造1対1、一列に並ぶ配列、連結リスト、スタック、キュー電車の車両、行列
ハッシュ構造キー→値のマッピングハッシュテーブル、辞書、セット図書館の索引カード
ツリー構造1対多、階層関係二分木、B木、ヒープ家系図、フォルダ
グラフ構造多対多、ネットワーク関係有向グラフ、無向グラフ地下鉄路線図、ソーシャルネットワーク

なぜこんなに多くの種類を学ぶのか?

万能なデータ構造は存在しないからです。各構造は「検索速度」「挿入速度」「メモリ使用量」の間でトレードオフを行っています。家具を運ぶのにバッグを使わないのと同じで、手紙を届けるのにトラックを使わないのと同じ——適切な道具を選べば、半分の労力で倍の成果が得られます。


2. 線形構造:最も基本的な整理方法

線形構造は最も直感的なデータ整理方法です——データが次々と並び、電車の車両のようです。しかし「どうつなぐか」と「どの端から操作するか」の違いによって、4つのバリエーションが生まれ、それぞれに得意技があります。

Four Forms of Linear StructuresHow arrays, linked lists, stacks, and queues differ
ArrayContiguous memory, indexed access
0
10
1
25
2
33
3
47
4
59
5
62
✓ Contiguous memory | ✓ Fast access (O(1)) | ✗ Slow insert/delete (O(n))
Operation Comparison
Data structureAccessInsertDeleteFeature
📊 ArrayO(1) fastO(n) slowO(n) slowFixed size
🔗 Linked listO(n) slowO(1) fastO(1) fastVariable size
🥞 StackO(n)O(1) fastO(1) fastOne-end operations
🚶 QueueO(n)O(1) fastO(1) fastTwo-end operations
Real-World Uses
📋
List data
Student scores or product price lists
🖼️
Image processing
Pixel matrix storage
📈
Charts
Data ordered by time

2.1 配列 vs 連結リスト:まったく異なる2つの保存方式

配列と連結リストは最も基本的な2つの線形構造です。その核心的な違いはメモリレイアウトにあります:

比較項目配列連結リスト
メモリレイアウト連続したひと塊散在し、ポインタでつなぐ
n番目へのアクセスアドレスを直接計算、O(1)先頭から順に探す、O(n)
中間への挿入後続要素をすべて移動、O(n)2つのポインタを書き換えるだけ、O(1)
サイズ作成時に固定いつでも拡張可能
日常での例え番号付きロッカー宝探しゲームの手がかりの連鎖

配列と連結リスト、どちらを使うべきか?

  • データ量が既知で、位置指定アクセスが頻繁 → 配列(例:成績表、ピクセル行列)
  • データ量が未知で、挿入・削除が頻繁 → 連結リスト(例:プレイリスト、UNDO履歴)
  • 迷ったら? → まずは配列。ほとんどのケースで、配列のキャッシュ親和性によるパフォーマンス上の利点が大きい

2.2 スタックとキュー:「ルール」を追加した線形構造

スタックとキューは本質的には配列か連結リストですが、操作方法に制限を加えたものです。機能は減ったように見えますが、その制限こそが明確な用途を生み出します:

構造ルール操作例えあなたのコードのどこにあるか?
スタック後入れ先出し (LIFO)push / pop積み重ねた皿関数コールスタック、ブラウザの戻る、Ctrl+Z のUNDO
キュー先入れ先出し (FIFO)enqueue / dequeueチケット購入の列タスクスケジューリング、メッセージキュー、印刷キュー

「制限」があるのはむしろ良いこと?

「皿を置く」「皿を取る」の2操作しかないスタックを想像してください——順番を間違えることは絶対にありません。制限は確定性をもたらし、確定性は信頼性をもたらします。 関数コールスタックは「後入れ先出し」によって、最後に呼ばれた関数が最初に戻ることを保証しています。途中の関数に任意にアクセスできたら、プログラムは混乱してしまうでしょう。


3. ハッシュテーブル:最速の検索

線形構造の検索はどれも十分に速くありません——配列は走査に O(n) かかり、ソート済みで二分探索を使っても O(log n) です。O(1) で直接見つけられる構造はないのでしょうか?あります、それがハッシュテーブルです。

Hash Tables: Super-Fast LookupFind data directly through a key
📚
A hash table is like a library index card: instead of searching shelf by shelf, you use the index to find the book location directly.
Store data
Hashing process
Input key
apple
Hash function
hash(key) % 10
Array index
0
Hash table
0
apple: apple
1
Empty
2
Empty
3
Empty
4
Empty
5
Empty
6
orange: orange
7
Empty
8
Empty
9
banana: banana
Performance Comparison
Hash table lookup
O(1)
Found instantly
Array lookup
O(n)
Requires traversal
Binary search
O(log n)
Requires sorted data
Common Uses
👤
User table (user ID → profile)
🛒
Shopping cart (product ID → quantity)
📝
Cache system (URL → page content)
🔍
Dictionary (word → definition)

3.1 ハッシュテーブルの核心的な考え方

ハッシュテーブルの原理はとてもシンプルです:

  1. キーを渡す(例:"apple")
  2. ハッシュ関数がキーを数値に変換(例:hash("apple") = 3
  3. 配列の3番目の位置に直接アクセス——走査不要、一発で到達

これは図書館の索引システムのようなものです:本棚を一列ずつ探す必要はなく、索引カードを見れば本の位置に直接たどり着けます。

3.2 ハッシュ衝突:2つのキーが衝突したら?

異なる2つのキーが同じインデックスを生成することがあります——これをハッシュ衝突と呼びます。2冊の本の索引番号が同じで、同じ位置を指しているようなものです。

解決方法原理例え
チェイン法同じ位置に連結リストで複数の値を格納同じロッカーに複数の本を入れる
オープンアドレス法衝突したら後ろの空き位置を探すロッカーがいっぱいなら隣のロッカーに入れる

3.3 ハッシュテーブルのパフォーマンス

操作平均ケース最悪ケース(すべて衝突)
検索O(1)O(n)
挿入O(1)O(n)
削除O(1)O(n)

いつ劣化するのか?

すべてのキーが同じインデックスにマッピングされると、ハッシュテーブルは連結リストに劣化し、すべての操作が O(n) になります。回避方法:優れたハッシュ関数の選択 + 動的リサイズ(負荷率がしきい値を超えたら拡張)。

ハッシュテーブルはあなたのコードのあらゆる場所に

  • JavaScript の {} オブジェクトと Map → ハッシュテーブル
  • Python の dict → ハッシュテーブル
  • Java の HashMap → ハッシュテーブル
  • データベースのインデックス → 内部でもハッシュを使用

user["name"]map.get("key") を書くたびに、その裏ではハッシュテーブルが働いています。


4. ツリー構造:階層関係の表現

ハッシュテーブルは検索が速いですが、データは順序付けられていません。高速な検索とデータの順序維持を両立したいなら、ツリー構造が必要です。

ツリーの核心的な特徴:各ノードは複数の「子」を持てますが、「親」は1つだけです(ルートノードを除く)。この1対多の階層関係は、現実のあらゆる場所で見られます。

Tree Structures: Representing HierarchiesAn organization model like a family tree
Choose a tree type:
50307020406080
Tree Structure Features
🌲
Hierarchy
Nodes have one-to-many parent-child relationships
🎯
Single root node
Except for the root, each node has exactly one parent
🔍
Efficient lookup
Binary search tree lookup is O(log n)
🔄
Multiple traversals
Preorder, inorder, postorder, and level-order traversal
Use Cases
📁
File systems
Hierarchical organization of folders and files
🌐
HTML DOM
Nested structure of web page elements
🏢
Organization charts
Company management hierarchy
🌲
Decision trees
Classification algorithms in machine learning

4.1 二分探索木:順序付きのツリー

二分探索木にはシンプルながら強力なルールがあります:左小右大

  • 左部分木のすべての値 < ルートノード
  • 右部分木のすべての値 > ルートノード

検索時、毎回の比較で半分のノードを除外でき、時間計算量は O(log n) です。数当てゲームのように——「50より大きい?小さい?」「大きい。」「75より大きい?小さい?」——毎回半分ずつ除外します。

4.2 平衡木:劣化を防ぐ

二分探索木には問題があります:データが順番に挿入されると(1, 2, 3, 4, 5)、木は一本の鎖に劣化し、検索が O(n) に戻ってしまいます。平衡木は構造を自動調整することでこの問題を回避します:

種類平衡戦略特徴典型的な用途
AVL木厳密な平衡(高さの差 ≤ 1)検索が最速、挿入・削除はやや遅い検索が頻繁なシーン
赤黒木近似的な平衡総合性能が良好Java TreeMap、Linuxカーネル
B木多分岐平衡、1ノードに複数の値を格納ディスクI/Oを削減データベースインデックス

ツリーはあなたのコードのどこにあるか?

  • ファイルシステム:フォルダのネストはツリー構造
  • HTML DOM<html><body><div><p> は一本のツリー
  • データベースインデックス:B+木により、数百万件のデータ検索がわずか3〜4回のディスク読み取りで完了
  • JSON/XML:ネストされたデータ形式は本質的にツリー

5. グラフ構造:複雑な関係のネットワーク

ツリーは「1対多」の階層関係しか表現できません。しかし現実には「多対多」の関係が多くあります——あなたの友達にも友達がいて、都市間には複数の道路があります。このような任意のノード間に接続があり得る構造が、グラフです。

Graph Structures: Representing Complex RelationshipsA network of nodes and edges
ABCDE
Graph properties
Vertices (V)
5
Edges (E)
6
Degree
2.4
Use Cases
🗺️Map navigation (shortest path)
👥Social networks (friend relationships)
🌐Web links (PageRank)
🔗Dependencies (package management)

5.1 グラフの3つの形態

種類特徴例え典型的な用途
無向グラフ辺に方向がない、A→B と B→A は同じLINEの友達(相互)ソーシャルネットワーク、通信ネットワーク
有向グラフ辺に方向がある、A→B と B→A は別Twitterのフォロー(一方向)Webリンク、依存関係
重み付きグラフ辺に重みがある(距離、コストなど)都市間の道路(距離付き)地図ナビゲーション、最短経路

5.2 グラフの探索

グラフの探索は線形構造より複雑です。循環(A→B→C→A)があり得るため、「訪問済み」ノードを記録する必要があります:

探索方法戦略例え適用シーン
BFS(幅優先探索)まずすべての隣接ノードを訪問し、次に隣接ノードの隣接ノードへ水の波紋の広がり最短経路、階層走査
DFS(深さ優先探索)一本道を突き当たりまで進み、行き止まりなら戻る迷路探索経路探索、連結性判定

グラフの現実での応用

  • 地図ナビゲーション:都市がノード、道路が辺、ナビゲーションはグラフ上の最短経路探索
  • ソーシャルネットワーク:ユーザーがノード、フォロー/友達が辺、「知り合いかも」はグラフアルゴリズムによる推薦
  • パッケージマネージャ:npm/pip の依存関係は有向グラフ、npm install はグラフのトポロジカルソート

6. パフォーマンス比較:全データ構造を一枚の表で

ここまで多くのデータ構造を学びましたが、それらのパフォーマンスは実際どれくらい違うのでしょうか?以下のインタラクティブな比較で直感を養えます:

Data Structures: Containers for DataChoose different storage models for different scenarios
ArrayContiguous memory and fast indexed access
010
120
230
340
450
560
670
780
Access arr[2] = O(1), insert/delete = O(n)
Time Complexity Comparison
OperationArrayLinked listHash tableTree
AccessO(1)O(n)O(1)O(log n)
SearchO(n)O(n)O(1)O(log n)
InsertO(n)O(1)O(1)O(log n)
DeleteO(n)O(1)O(1)O(log n)
Core idea:Data structures are containers for data. Different containers have different tradeoffs. Choosing the right data structure can improve program efficiency by orders of magnitude.

主要パフォーマンス比較表:

データ構造アクセス検索挿入削除空間
配列O(1)O(n)O(n)O(n)O(n)
連結リストO(n)O(n)O(1)O(1)O(n)
スタック/キューO(n)O(n)O(1)O(1)O(n)
ハッシュテーブルO(1)O(1)O(1)O(n)
二分探索木O(log n)O(log n)O(log n)O(n)
グラフO(V+E)O(1)O(E)O(V+E)

この表の読み方

  • O(1):データ量に関わらず操作時間は一定——最速
  • O(log n):データ量が2倍になっても、時間は1ステップ増えるだけ——かなり速い
  • O(n):データ量が2倍になると、時間も2倍——普通
  • O(V+E):ノード数と辺数に依存——グラフ特有の表現

注意:これらはすべて平均ケースです。最悪ケースでは、ハッシュテーブルは O(n) に劣化し、二分探索木も O(n) に劣化します。


7. 選定ガイド:どのデータ構造を使うべきか?

多くのデータ構造を学びましたが、実際の要件に直面したとき、どう選べばよいでしょうか?鍵は要件から出発し、自分にいくつかの質問をすることです:

  1. 最も頻繁な操作は何か? 検索?挿入?削除?走査?
  2. データ間の関係は? 1対1?1対多?多対多?
  3. データ量はどの程度か? 数十件と数百万件では最適な選択がまったく異なる場合がある
  4. 順序付けが必要か? 特定の順序でデータを走査する必要があるか
How Do You Choose the Right Data Structure?Make the best choice based on scenario needs
What is your use case?
🔍
Fast lookup
Find matching data quickly by key
📊
Preserve order
Data must stay in insertion order or a specific order
🥞
Last in, first out
The most recent item is processed first
🚶
First in, first out
Earlier items are processed first
🌳
Hierarchy
Data has parent-child relationships
🕸️
Complex relationships
Data has many-to-many connections
Quick Reference
Scenario needRecommended structureTime complexity
Random accessArrayO(1)
Fast lookupHash tableO(1)
Ordered lookupBinary search treeO(log n)
Frequent insertion/deletionLinked listO(1)
Undo operationsStackO(1)
Task schedulingQueueO(1)
Decision Flow
Need fast element access?
Yes
Array / Hash table
No
Need frequent insertion and deletion?
Yes
Linked list
No
Need to preserve order?
Yes
Stack / Queue
No
Tree / Graph

クイック意思決定フロー:

あなたの要件推奨構造理由
位置指定で高速アクセス配列O(1) のランダムアクセス
中間での頻繁な挿入・削除連結リストO(1) の挿入・削除、要素の移動不要
後入れ先出し(UNDO、再帰)スタックLIFOセマンティクスが自然に適合
先入れ先出し(タスクキュー)キューFIFOセマンティクスが自然に適合
キーによる高速検索ハッシュテーブルO(1) の平均検索
順序付きデータ + 高速検索二分探索木O(log n) 検索かつ順序維持
複雑な多対多の関係グラフ任意のノード間の接続を表現可能

実践開発における経験則

  • 80%のシーンは配列とハッシュテーブルで十分
  • 順序が必要ならツリーを検討
  • 関係が複雑ならグラフを検討
  • 迷ったら? 最もシンプルなものから始め、パフォーマンス問題が出てから切り替える。早すぎる最適化は諸悪の根源

まとめ

データ構造はプログラムの骨格です。配列は番号付きロッカーのように、位置で取り出すのが最速;連結リストは宝探しの手がかりの連鎖のように、挿入・削除が最も柔軟;ハッシュテーブルは図書館の索引のように、名前で探すのが最速;ツリーは家系図のように、階層関係を表現しつつ順序を維持;グラフは地下鉄路線図のように、任意の複雑なネットワーク関係を表現します。最善のデータ構造はなく、最適なデータ構造があるだけ——鍵は各構造の利点とコストを理解し、実際の要件に基づいてトレードオフを行うことです。


さらに学ぶ

テーマおすすめリソース
データ構造の可視化VisuAlgo - 様々なデータ構造とアルゴリズムのアニメーション解説
アルゴリズムとデータ構造『アルゴリズム図鑑』- Aditya Bhargava、図解が豊富で入門に最適
深く理解する『データ構造とアルゴリズム解析』- Mark Allen Weiss
演習問題LeetCode - データ構造別に分類された練習問題

次のステップ

データ構造の核心的な知識を身につけました。次に学べる内容:

  • アルゴリズム思考:ソート、探索、再帰、動的計画法などのアルゴリズム思考を使って問題を解決する
  • プログラミング言語:様々なプログラミング言語がこれらのデータ構造をどのように実装しているかを理解する