Java | ソートアルゴリズムを体系的に学ぶ

Java Java
スポンサーリンク

Javaで学ぶ「アルゴリズムとデータ構造」(前半)

アルゴリズムとデータ構造は、Javaに限らずすべてのプログラミング言語で「土台」になる考え方です。 文法を覚えるだけでは、コードは書けても「うまい設計」や「速い処理」にはなかなか到達できません。 前半では、まずデータ構造とアルゴリズムの関係を整理し、 Javaでよく使う基本的なデータ構造(配列・ArrayListLinkedList・スタック・キュー)を、 初心者向けにかみ砕いて解説していきます。 後半では、木構造・ハッシュ・グラフなど、より本格的な構造と探索アルゴリズムに踏み込みます。

データ構造とアルゴリズムの関係をイメージでつかむ

まず、「データ構造」と「アルゴリズム」の関係を言葉で整理しておきます。 データ構造は「データをどう持つか」、アルゴリズムは「そのデータをどう扱うか」です。 例えば、同じ「10件の数値」を扱うとしても、 配列で持つのか、リストで持つのか、キューで持つのかによって、 「取り出しやすさ」「追加しやすさ」「削除しやすさ」が変わります。

アルゴリズムは、そのデータ構造の上で動く「手順」です。 ソート、検索、探索、集計、フィルタリングなどはすべてアルゴリズムです。 つまり、データ構造を選ぶことは「アルゴリズムの土台を選ぶこと」であり、 アルゴリズムを選ぶことは「その土台の上でどう動くかを決めること」です。

この関係を意識できるようになると、 「とりあえず配列」「とりあえず ArrayList」ではなく、 目的に応じて構造と手順を選べるようになっていきます。

配列(int[])という一番素朴なデータ構造

Javaで最も基本的なデータ構造は「配列」です。 配列は「固定長の箱の並び」で、 インデックス(0, 1, 2, …)を指定して要素にアクセスします。

簡単な例を見てみます。

int[] scores = new int[5];
scores[0] = 80;
scores[1] = 90;
scores[2] = 75;
scores[3] = 88;
scores[4] = 92;

System.out.println(scores[1]); // 90
Java

配列の特徴は次のようなものです。

長さが固定で、途中で増やしたり減らしたりできない インデックスを指定したアクセスが非常に速い メモリ上に連続して並ぶため、CPUにとって扱いやすい

アルゴリズムの世界では、 「配列に対してソートする」「配列に対して線形探索する」 といった形で、配列は最も基本的な土台になります。

ただし、要素数が変わるような場面では、 配列だけでは扱いづらくなります。 そこで登場するのが ArrayListLinkedList です。

ArrayList:可変長の配列としてのリスト

ArrayList は、「中身は配列だが、長さを増やしたり減らしたりできる」リストです。 Javaのコレクションの中でも、最もよく使われるクラスの一つです。

簡単な例を見てみます。

import java.util.ArrayList;

ArrayList<Integer> scores = new ArrayList<>();
scores.add(80);
scores.add(90);
scores.add(75);

System.out.println(scores.get(1)); // 90

scores.add(88); // 要素数を増やせる
scores.remove(0); // 先頭を削除できる
Java

ArrayList の本質は、「配列をラップして、使いやすくしたもの」です。 内部では配列を使っていて、要素数が増えるときには 新しい配列を確保してコピーすることで「可変長」を実現しています。

重要なポイントは、 インデックスアクセス(get(i))が速い一方で、 途中の要素を削除したり挿入したりすると、 後ろの要素をずらす必要があり、コストがかかるという点です。

アルゴリズムの観点では、 「ランダムアクセスが多い」「末尾に追加することが多い」 といった場面で ArrayList が向いています。

LinkedList:つながりで表現するリスト

LinkedList は、「要素同士がポインタでつながったリスト」です。 配列のように「連続した箱」ではなく、 「ノードが鎖のようにつながっている」イメージです。

簡単な例を見てみます。

import java.util.LinkedList;

LinkedList<Integer> scores = new LinkedList<>();
scores.add(80);
scores.add(90);
scores.add(75);

System.out.println(scores.get(1)); // 90

scores.addFirst(70); // 先頭に追加
scores.addLast(100); // 末尾に追加
scores.remove(2); // 真ん中を削除
Java

LinkedList の特徴は次のようなものです。

先頭や末尾の追加・削除が速い 途中の要素の追加・削除も、位置さえ分かれば速い インデックスアクセス(get(i))は、内部で前から順にたどるため遅い

内部的には、各要素が「前の要素」「次の要素」への参照を持つノードとして表現されています。 そのため、「つなぎ替え」が簡単で、 「途中に挿入」「途中を削除」といった操作が得意です。

アルゴリズムの観点では、 「順番にたどることが多い」「途中の挿入・削除が多い」 といった場面で LinkedList が向いています。

スタック(Stack):「後から入れたものが先に出る」構造

スタックは、「後から入れたものが先に出る(LIFO)」構造です。 本を積み重ねて、上から取り出すイメージが近いです。

Javaでは、古い Stack クラスもありますが、 最近は Deque(両端キュー)をスタックとして使うことが多いです。

簡単な例を見てみます。

import java.util.ArrayDeque;
import java.util.Deque;

Deque<String> stack = new ArrayDeque<>();
stack.push("A");
stack.push("B");
stack.push("C");

System.out.println(stack.pop()); // C
System.out.println(stack.pop()); // B
System.out.println(stack.pop()); // A
Java

スタックは、アルゴリズムの世界で非常に重要な役割を持ちます。

再帰の展開を自分で管理したいとき 式の評価(逆ポーランド記法など) 括弧の整合性チェック 深さ優先探索(DFS)の非再帰実装

など、多くの場面でスタックが登場します。

「最後に入れたものが最初に出る」という性質を理解しておくと、 アルゴリズムの設計が一気に楽になります。

キュー(Queue):「先に入れたものが先に出る」構造

キューは、「先に入れたものが先に出る(FIFO)」構造です。 行列に並んで、先に並んだ人から順に処理されるイメージです。

Javaでは、ArrayDequeLinkedList をキューとして使うことが多いです。

簡単な例を見てみます。

import java.util.ArrayDeque;
import java.util.Queue;

Queue<String> queue = new ArrayDeque<>();
queue.offer("A");
queue.offer("B");
queue.offer("C");

System.out.println(queue.poll()); // A
System.out.println(queue.poll()); // B
System.out.println(queue.poll()); // C
Java

キューは、アルゴリズムの世界で次のような場面で使われます。

幅優先探索(BFS) タスクの順番待ち メッセージキュー イベント処理

「先に来たものから順に処理する」という性質は、 現実世界の多くの仕組みと対応しています。 そのため、キューを理解することは、 アルゴリズムだけでなくシステム設計の理解にもつながります。

基本データ構造とアルゴリズムのつながりを意識する

ここまでに登場した

配列 ArrayList LinkedList スタック キュー

は、どれも「Javaでアルゴリズムを書くときの土台」になります。

例えば、

配列や ArrayList に対してソートアルゴリズムを適用する キューを使って幅優先探索(BFS)を実装する スタックを使って深さ優先探索(DFS)を非再帰で書く LinkedList を使って途中挿入・削除の多い処理を効率化する

といった形で、データ構造とアルゴリズムは常にセットで登場します。

「どの構造の上で、どのアルゴリズムを動かすか」を意識できるようになると、 コードの質とパフォーマンスが大きく変わっていきます。

前半のまとめと後半への橋渡し

前半では、Javaのアルゴリズムとデータ構造を学ぶための土台として

データ構造とアルゴリズムの関係 配列(int[])の特徴と役割 ArrayListLinkedList の違いと使いどころ スタック(LIFO)の性質とアルゴリズムでの役割 キュー(FIFO)の性質とアルゴリズムでの役割

を整理しました。

後半では、さらに一歩進んで

木構造(ツリー)と探索アルゴリズム ハッシュテーブル(HashMap)と高速検索 グラフ構造と DFS/BFS 計算量(O(n), O(n log n), O(1))の直感 セキュリティ・パフォーマンスの観点から見た「危険なデータ構造の使い方」

といったテーマを、具体的な Java コードとともに深掘りしていきます。

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