導入
データ構造は、効率的なアルゴリズムを実現するための基盤です。特に、業務アプリケーションでは、データの取り扱いや処理の効率が求められます。本記事では、中級レベルのTypeScriptを使用して、実務で遭遇しやすいデータ構造の具体的なケースを取り上げ、その運用方法を探ります。特に、ツリー構造の一種であるヒープを取り上げ、具体的な利用シーンを通じて理解を深めます。
教科書レベルの解説(データ構造)
重要な概念の整理
ヒープは、完全二分木の特性を持ちながら、特定の順序を保つデータ構造です。最大ヒープでは親ノードが子ノードよりも大きく、最小ヒープではその逆になります。ヒープは優先度キューの実装によく利用され、タスクの優先順位に基づいて処理を行う際に非常に有用です。このデータ構造を使用することで、効率的な要素の挿入や削除が可能になります。
コード例(TypeScript)
class MaxHeap {
private heap: number[] = [];
insert(num: number): void {
this.heap.push(num);
this.bubbleUp();
}
private bubbleUp(): void {
let index = this.heap.length - 1;
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);
if (this.heap[index] <= this.heap[parentIndex]) break;
[this.heap[index], this.heap[parentIndex]] = [this.heap[parentIndex], this.heap[index]];
index = parentIndex;
}
}
extractMax(): number | null {
if (this.heap.length === 0) return null;
const max = this.heap[0];
const end = this.heap.pop()!;
if (this.heap.length > 0) {
this.heap[0] = end;
this.bubbleDown();
}
return max;
}
private bubbleDown(): void {
let index = 0;
const length = this.heap.length;
const element = this.heap[0];
while (true) {
let leftChildIndex = 2 * index + 1;
let rightChildIndex = 2 * index + 2;
let leftChild, rightChild;
let swap = null;
if (leftChildIndex < length) {
leftChild = this.heap[leftChildIndex];
if (leftChild > element) {
swap = leftChildIndex;
}
}
if (rightChildIndex < length) {
rightChild = this.heap[rightChildIndex];
if ((swap === null && rightChild > element) ||
(swap !== null && rightChild > leftChild!)) {
swap = rightChildIndex;
}
}
if (swap === null) break;
[this.heap[index], this.heap[swap]] = [this.heap[swap], this.heap[index]];
index = swap;
}
}
}
コードの行ごとの解説
- class MaxHeap { – ヒープのクラスを定義します。
- private heap: number[] = []; – ヒープを表す配列を初期化します。
- insert(num: number): void { – ヒープに新しい要素を挿入するメソッドです。
- this.heap.push(num); – 新しい要素を配列の末尾に追加します。
- this.bubbleUp(); – 挿入後に要素の位置を調整します。
- private bubbleUp(): void { – 要素を上に移動させるプライベートメソッドです。
- extractMax(): number | null { – 最大値を取り出すメソッドです。
- this.heap.pop()! – ヒープの最後の要素を取り出します。
- this.bubbleDown(); – 最大値を取り出した後に要素の位置を調整します。
- private bubbleDown(): void { – 要素を下に移動させるプライベートメソッドです。
練習問題編
以下の練習問題に挑戦し、ヒープの理解を深めましょう。
- 問題1: ヒープに10, 20, 5, 30, 15を挿入した後のヒープの状態を示してください。
- 模範解答: [30, 20, 5, 10, 15] です。
- 問題2: ヒープから最大値を取り出した後の状態を示してください。
- 模範解答: [20, 15, 5, 10] です。
- 問題3: ヒープに新たに25を挿入した後の状態を示してください。
- 模範解答: [25, 20, 5, 10, 15] です。
- 問題4: ヒープから最大値を取り出した後、再度最大値を取り出した際の状態を示してください。
- 模範解答: [20, 15, 5] です。
まとめ
- ヒープは優先度キューの実装に適したデータ構造です。
- 挿入や削除の操作が効率的に行えるため、実務において非常に有用です。
- 具体的なケーススタディを通じて、ヒープの使い方を理解することが重要です。