1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

《アルゴリズムを学ぶ》19.ヒープ

Posted at

Overview

ヒープ(Heap) は、最小値または最大値を効率的に管理できるデータ構造 であり、優先度付きキュー(Priority Queue) を実現するために使われる。
Python では heapq モジュールを使うと、最小ヒープ(Min-Heap)を簡単に実装 できる。
1.ヒープの基本
2.最大ヒープ(Max-Heap)の実装
3.ヒープの応用
4.典型問題を解いてみる

1. ヒープの基本

(1) ヒープとは?

  • 最小ヒープ(Min-Heap): 常に最小の要素が heap[0] に保持される

  • 最大ヒープ(Max-Heap): 常に最大の要素が heap[0] に保持される

  • ヒープは「完全二分木(Complete Binary Tree)」
    二分木(Binary Tree): 各ノードが 最大2つの子 を持つ木
    完全二分木(Complete Binary Tree):左から順に詰めて ノードを配置する
    最下段以外はすべて埋まっている
    最小値(または最大値)がルート(頂点)に来る
    親ノードの値は子ノードの値より小さい(または大きい)
    例: 最小ヒープ(Min-Heap)の木構造

          1
         / \
        3   4
       / \  /
      7   6 5
    
  • 優先度付きキューを実現するために使用される

(2) Python の heapq の基本操作

import heapq

heap = []

# 要素を追加(push)
heapq.heappush(heap, 3)
heapq.heappush(heap, 1)
heapq.heappush(heap, 4)

print(heap)  # [1, 3, 4](最小値が先頭にくる)

# 最小値を取得(pop)
print(heapq.heappop(heap))  # 1 最小値を取得しながら削除
print(heap)  # [3, 4]
  • リストとは異なり、順番が保証されるのは最小値のみ

2. 最大ヒープ(Max-Heap)の実装

Python の heapq は デフォルトで最小ヒープ なので、最大ヒープを作るには 負の値を使う。

import heapq

heap = []

# 負の値を追加(最大値を管理)
heapq.heappush(heap, -3)
heapq.heappush(heap, -1)
heapq.heappush(heap, -4)

# 最大値を取得
print(-heapq.heappop(heap))  # 4
print(-heapq.heappop(heap))  # 3
  • 負の値を入れることで、最大値が heap[0] に来る
  • heapq.heappop() の戻り値に -1 をかけると、元の正の値を取得できる

3. ヒープの応用

(1) N 個の要素の中から K 番目に小さい要素を求める

import heapq

arr = [7, 10, 4, 3, 20, 15]
k = 3

heapq.heapify(arr)  # 配列をヒープ化
for _ in range(k - 1):
    heapq.heappop(arr)  # k-1 回 pop する

print(heapq.heappop(arr))  # 3番目に小さい値(7)

(2) N 個の要素の中から K 個の最大値を求める

import heapq

arr = [7, 10, 4, 3, 20, 15]
k = 3

print(heapq.nlargest(k, arr))  # [20, 15, 10]
  • heapq.nlargest(k, arr) を使うと、最大 K 個の要素を O(K log N) で取得

(3) 2つの最小値を合成する(最小コストで作る)

import heapq

N = int(input())  # 数列の要素数
A = list(map(int, input().split()))  # 数列

heapq.heapify(A)  # 配列を最小ヒープに変換

cost = 0  # 合成コストの合計
while len(A) > 1:
    x = heapq.heappop(A)  # 最小値を取り出す
    y = heapq.heappop(A)  # 2番目に小さい値を取り出す
    cost += x + y  # 合成コストを追加
    heapq.heappush(A, x + y)  # 合成した値をヒープに戻す

print(cost)  # 最小合成コスト
  • 貪欲法(Greedy Algorithm)を用いた最小コスト合成アルゴリズム
  • ロジックの動作手順
  •   # 例として、以下のリストを考えます:
      A = [1, 2, 3, 4, 5]
    
      # ステップ 1: 初期状態
      heapq.heapify(A) # 最小ヒープに変換:[1, 2, 3, 4, 5] (最小値が先頭)
    
      # ステップ2〜5はwhile内の処理
      # ステップ 2: 2つの最小値を合成
      # 1. 最小値 1 を取り出す
      # 2. 次に小さい 2 を取り出す
      # 3. 1 + 2 = 3 を合成コストに加算(現在の合成コスト: 3)
      # 4. 3 をヒープに戻す
    
      # ヒープ: [3, 3, 5, 4]
    
      # ステップ 3: 2つの最小値を合成
      # 1. 3 を取り出す
      # 2. 3 を取り出す
      # 3. 3 + 3 = 6 を合成コストに加算(合計: 3 + 6 = 9)
      # 4. 6 をヒープに戻す
    
      # ヒープ: [4, 5, 6]
    
      # ステップ 4: 2つの最小値を合成
      # 1. 4 を取り出す
      # 2. 5 を取り出す
      # 3. 4 + 5 = 9 を合成コストに加算(合計: 9 + 9 = 18)
      # 4. 9 をヒープに戻す
    
      # ヒープ: [6, 9]
    
      # ステップ 5: 最後の 2 つを合成
      # 1. 6 を取り出す
      # 2. 9 を取り出す
      # 3. 6 + 9 = 15 を合成コストに加算(合計: 18 + 15 = 33)
    
      # ヒープ: [](すべて合成完了)
    
      # 最終的な合成コスト: 33
    

4. 典型問題を解いてみる

例題1. K 番目に小さい値を求める

問題: N 個の整数 A から K 番目に小さい値を求めよ。

参考: ABC315 B - Kth Smallest Element

回答 
import heapq

N, K = map(int, input().split())
A = list(map(int, input().split()))

heapq.heapify(A)
for _ in range(K - 1):
    heapq.heappop(A)

print(heapq.heappop(A))

例題2. K 個の最大値を求める

問題: N 個の整数 A から K 個の最大値を求めよ。

参考: ABC318 B - K Largest Elements

回答 
import heapq

N, K = map(int, input().split())
A = list(map(int, input().split()))

print(*heapq.nlargest(K, A))

例題3. 優先度付きキューのシミュレーション

問題: N 個のクエリが与えられる。insert x で x を追加し、pop で最小値を取り出せ。

参考: ABC319 C - Priority Queue

回答 
import heapq

N = int(input())
heap = []

for _ in range(N):
    query = input().split()
    if query[0] == "insert":
        heapq.heappush(heap, int(query[1]))
    elif query[0] == "pop":
        print(heapq.heappop(heap))

例題4. 2つの最小値を合成する

問題: N 個の数を最小コストで合成せよ。

参考: ABC320 B - Minimum Cost Merge

回答 
import heapq

N = int(input())
A = list(map(int, input().split()))
heapq.heapify(A)

cost = 0
while len(A) > 1:
    x = heapq.heappop(A)
    y = heapq.heappop(A)
    cost += x + y
    heapq.heappush(A, x + y)

print(cost)
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?