Pythonで最大ヒープ(Max Heap)を実装してみた

# はじめに
こんにちは！今回はPythonで「最大ヒップ(Max Heap)」を実装し、ヒープ操作の基本である挿入(add)と削除(delete)の方法を紹介します。ヒープはデータ楮のひとつで、特に、優先度付きキューやソートアルゴリズムに使われる重要な概念です。
# ヒープとは？
ヒープは「完全二分木」という構造で表現されるデータ構造です。今回扱う最大ヒープ(Max Heap)では、親ノードの値が常に子ノードの値以上であることが条件になります。
つまり、ルート(最上位)の要素が常にヒープ内で最大値となります。
# 実装コード
以下は、Pythonで最大ヒープを構築するためのクラスMax Heapの実装です。メソッドごとの説明も加えています。
```py:Max_Heap.py
class MaxHeap:
    def __init__(self, initial_values=None):
        """ ヒープを初期化。初期値が指定されている場合はすべてヒープに追加する """
        self.heap = []
        if initial_values:
            for value in initial_values:
                self.add_heap(value)

    def add_heap(self, value):
        """ ヒープに新しい値を追加し、ヒープ条件を満たすよう調整する """
        self.heap.append(value)  # 値を末尾に追加
        self._heapify_up(len(self.heap) - 1)  # 上方向に調整

    def delete_heap(self):
        """ ヒープのルート（最大値）を削除し、ヒープ条件を満たすよう調整する """
        if len(self.heap) == 0:
            raise IndexError("空のヒープからは削除できません")
        if len(self.heap) == 1:
            return self.heap.pop()
        root_value = self.heap[0]
        self.heap[0] = self.heap.pop()  # 末尾の要素をルートに移動
        self._heapify_down(0)  # 下方向に調整
        return root_value

    def _heapify_up(self, index):
        """ 新しく追加された要素を適切な位置まで上方向に調整する """
        parent_index = (index - 1) // 2
        while index > 0 and self.heap[index] > self.heap[parent_index]:
            self.heap[index], self.heap[parent_index] = self.heap[parent_index], self.heap[index]
            index = parent_index
            parent_index = (index - 1) // 2

    def _heapify_down(self, index):
        """ ルート要素を適切な位置まで下方向に調整する """
        child_index = 2 * index + 1
        while child_index < len(self.heap):
            right_child_index = child_index + 1
            if right_child_index < len(self.heap) and self.heap[right_child_index] > self.heap[child_index]:
                child_index = right_child_index
            if self.heap[index] >= self.heap[child_index]:
                break
            self.heap[index], self.heap[child_index] = self.heap[child_index], self.heap[index]
            index = child_index
            child_index = 2 * index + 1
```
# 実行例
次に、実際にMax Heapクラスを使ってヒープを操作してみます。初期値として[6,4,2,1]を持つヒープに対し、次の操作を順番に行います。
- 新しい値を追加(add_heap)
- 最大値を削除(delete_heap)
```py:Max_Heap.py
heap = MaxHeap([6, 4, 2, 1])
print("初期ヒープ:", heap.heap)

operations = [7, 8, 5, "delete", 4, 6, 9, "delete", "delete"]

print(f"\t\t\t     i :  {'  '.join(list(map(str, range(9))))}")
for i, operation in enumerate(operations, 1):
    if operation == "delete":
        max_value = heap.delete_heap()
        print(f"{i}. delete_heap(H) \t-> H[i]: {heap.heap} \t-> {max_value}")
    else:
        heap.add_heap(operation)
        print(f"{i}. add_heap(H, {operation}) \t-> H[i]: {heap.heap}")
```
# 実行結果
```
初期ヒープ: [6, 4, 2, 1]
			     i :  0  1  2  3  4  5  6  7  8
1. add_heap(H, 7) 	-> H[i]: [7, 6, 2, 1, 4]
2. add_heap(H, 8) 	-> H[i]: [8, 6, 7, 1, 4, 2]
3. add_heap(H, 5) 	-> H[i]: [8, 6, 7, 1, 4, 2, 5]
4. delete_heap(H) 	-> H[i]: [7, 6, 5, 1, 4, 2] 	-> 8
5. add_heap(H, 4) 	-> H[i]: [7, 6, 5, 1, 4, 2, 4]
6. add_heap(H, 6) 	-> H[i]: [7, 6, 5, 6, 4, 2, 4, 1]
7. add_heap(H, 9) 	-> H[i]: [9, 7, 5, 6, 4, 2, 4, 1, 6]
8. delete_heap(H) 	-> H[i]: [7, 6, 5, 6, 4, 2, 4, 1] 	-> 9
9. delete_heap(H) 	-> H[i]: [6, 6, 5, 1, 4, 2, 4] 	-> 7
```
# コードの説明
- add_heap メソッド 
値をヒープに追加し、ヒープ条件を満たすように「上方向」に要素を調整します。
- delete_heap メソッド
ルート要素(最大値)を削除し、末尾の要素をルートに移動してから「下方向」に要素を調整します。
# まとめ
今回のプログラムは、最大ヒープをPythonで実装し、その基本的な操作であるaddとdeleteを紹介しました。ヒープソートや優先度付きキューなど、ヒープはさまざまなアルゴリズムで活用される重要なデータ構造です。最後まで読んでくださり、ありがとうございました。もし改善点や質問があれば、ぜひコメントしてください！