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

【C++】最小ヒープからk番目に小さい要素を求める方法

このチュートリアルでは、最小ヒープ(min-heap)からk番目に小さい要素を求めるプログラムをC++で作成します。

この問題は、優先度付きキュー(priority queue)を利用することで効率的に解くことができます。まずは、プログラムを完成させるまでの手順を確認していきましょう。

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

アルゴリズムのポイント

この手法が正しく動作するのは、最小ヒープには「親ノードの値は必ず子ノード以下である」という性質が成り立つためです。各ノードの子だけを順次キューに追加していくことで、ヒープ全体をソートすることなく、小さい方から順にk個の要素だけを取り出すことができます。

計算量はO(k log k)となり、ヒープ全体を並べ替えてからk番目を求める方法(全体でO(n log n))よりも効率的です。

コード例

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

#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 findKthSmallestElement(Heap &heap, int k) {
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<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>{ 10, 14, 19, 24, 32, 41, 27, 44, 35, 33 };
    cout << findKthSmallestElement(heap, 4) << endl;
    return 0;
}

実行結果

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

24

サンプルのヒープ {10, 14, 19, 24, 32, 41, 27, 44, 35, 33} を昇順に並べると「10 → 14 → 19 → 24 → …」となるため、4番目に小さい要素である「24」が出力されます。

まとめ

本チュートリアルでは、優先度付きキューを活用して、最小ヒープから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