C++
 Computer >> コンピューター >  >> プログラミング >> C++

【C++】最大ヒープからK番目に大きい要素を優先度付きキューで効率よく求める方法

このチュートリアルでは、最大ヒープ(max-heap)から K番目に大きい要素 を取り出すプログラムをC++で実装します。

この問題は、優先度付きキュー(priority queue)を使うことで、ヒープ全体をソートすることなく効率的に解くことができます。アルゴリズムの流れは以下の通りです。

アルゴリズムの手順

  • 最大ヒープを正しい値で初期化します。
  • 優先度付きキューを作成し、ヒープのルートノードを挿入します。
  • ループを k - 1 回繰り返します。
    • キューから最大の要素を取り出します(pop)。
    • 取り出したノードの左の子と右の子を優先度付きキューに追加します。
  • ループ終了後、優先度付きキューの先頭にある要素が K番目に大きい要素となります。
  • その値を返します。

なぜこの方法が有効か

最大ヒープでは親ノードが必ず子ノード以上の値を持つため、「次に大きい候補」は常に「これまでに取り出したノードの子」の中にしか存在しません。そのため、配列全体をソートする O(n log n) のアプローチと比べ、必要な部分だけを探索するこの手法は O(k log k) で済み、k が小さい場合に非常に効率的です。

実装例

それでは、実際のコードを見てみましょう。

#include <bits/stdc++.h>
using namespace std;

struct Heap {
    vector<int> elements;
    int n;
    Heap(int i = 0) : n(i) {
        elements = vector<int>(n);
    }
};

inline int leftIndex(int i) {
    return 2 * i + 1;
}

inline int rightIndex(int i) {
    return 2 * i + 2;
}

int findKthGreatestElement(Heap &heap, int k) {
    // ペア(値, インデックス)を格納する優先度付きキュー
    priority_queue<pair<int, int>> queue;
    queue.push(make_pair(heap.elements[0], 0));

    for (int i = 0; i < k - 1; ++i) {
        int node = queue.top().second;
        queue.pop();
        int left = leftIndex(node), right = rightIndex(node);
        if (left < heap.n) {
            queue.push(make_pair(heap.elements[left], left));
        }
        if (right < heap.n) {
            queue.push(make_pair(heap.elements[right], right));
        }
    }
    return queue.top().first;
}

int main() {
    Heap heap(10);
    heap.elements = vector<int>{ 44, 42, 35, 33, 31, 19, 27, 10, 26, 14 };
    cout << findKthGreatestElement(heap, 4) << endl;
    return 0;
}

コードのポイント

  • 優先度付きキューには pair<int, int> を格納し、値とインデックスをセットで管理しています。こうすることで、pop したノードから子ノードの位置を特定できます。
  • 子のインデックスがヒープのサイズ n を超えないよう、範囲チェックを行ってから挿入しています。

実行結果

上記のコードを実行すると、次の出力が得られます。

33

サンプルのヒープ {44, 42, 35, 33, ...} の中で、4番目に大きい要素は 33 であるため、正しく動作していることが確認できます。

まとめ

本記事では、優先度付きキューを活用して最大ヒープから K番目に大きい要素を求める方法を解説しました。全要素をソートせずに済むため、k が小さいケースでは特に高速に動作します。チュートリアルについて質問がある場合は、コメント欄でお気軽にお尋ねください。

  1. C++で最大ヒープから最小値の要素を見つける方法

    問題の概要最大ヒープ(max heap)の中から、最も小さい値を持つ要素を探す方法を解説します。以下のような最大ヒープを例に考えてみましょう。最大ヒープでは、親ノードの値は必ずその子ノードの値以上になります。この性質により、最小値は必ず葉ノード(leaf node)のいずれかに存在すると結論できます。ヒープが n 個のノードを含む場合、葉ノードの数は ceil(n/2) 個になります。また、最大ヒープは完全二分木であるため、配列として表現することができます。このとき、最初の葉ノードは floor(n/2) のインデックス以降に配置されます。上記の例では、最初の葉ノードはインデックス 5 に存在

  2. C++でヒープソートを実装する方法:最小ヒープの完全ガイド

    ヒープとはヒープ(Heap)とは、完全二分木のデータ構造であり、「最小ヒープ(Min Heap)」または「最大ヒープ(Max Heap)」のいずれかに分類されます。最大ヒープでは、ルートノードのキーがヒープ内のすべてのキーの中で最大値である必要があり、この性質は木の中のすべてのノードに対して再帰的に成り立たなければなりません。最小ヒープはその逆で、親ノードの値が常に子ノードの値以下になるという性質を持ちます。本記事では、最小ヒープを用いたヒープソートの実装方法を解説します。実装する関数の概要今回実装するクラスには、以下の主要なメンバ関数が含まれています。void BHeap::Insert(i