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

C++で整数配列の中央値を求めるプログラム【2つのヒープによる効率的な実装】

ここでは、MedianClass というクラスを実装することを考えます。このクラスには、次の2つのメソッドを持たせます。

  • add(value):データ構造に新しい値を追加します。

  • median():現在データ構造に格納されているすべての数値の中央値を求めます。

例えば、5、3、8 の順に値を追加した後で中央値を取得すると、出力は 5.0 になります。その後、さらに 9 を追加して中央値を取得すると、出力は 6.5 になります。

解き方のアプローチ

この問題は、2つの優先度付きキュー(ヒープ)を組み合わせることで効率的に解けます。片方のヒープには小さい方半分の値を、もう片方には大きい方半分の値を保持し、常に両者のバランスを保つことで、中央値を O(1) で取り出せるようにします。

具体的には、以下の手順に従います。

  • 優先度付きキュー left(最大ヒープ)と right(最小ヒープ)を定義します。

  • addNum メソッドを定義します。数値を入力として受け取り、次のように処理します。

  • left が空であるか、num が left の先頭要素より小さい場合は、

    • num を left に挿入します。

  • それ以外の場合は、

    • num を right に挿入します。

  • left のサイズが right のサイズより小さい場合は、

    • temp := right の先頭要素

    • right から要素を削除する

    • temp を left に挿入する

  • left のサイズ − right のサイズ > 1 となる場合は、

    • temp := left の先頭要素

    • left から要素を削除する

    • temp を right に挿入する

  • findMedian メソッドを定義します。このメソッドは次のように動作します。

left のサイズが right のサイズより大きい場合は left の先頭要素をそのまま返し、それ以外の場合は (left の先頭要素 + right の先頭要素) ÷ 2 を返します。

それでは、理解を深めるために以下の実装例を見てみましょう。

C++での実装例

#include <bits/stdc++.h>
using namespace std;
typedef double lli;
class MedianClass {
   priority_queue <int> left;
   priority_queue <int, vector <int>, greater<int>> right;
   public:
   void addNum(int num) {
      if(left.empty() || num<left.top()){
         left.push(num);
      }else right.push(num);
      if(left.size()<right.size()){
         lli temp = right.top();
         right.pop();
         left.push(temp);
      }
      if(left.size()-right.size()>1){
         lli temp = left.top();
         left.pop();
         right.push(temp);
      }
   }
   double findMedian() {
      return
      left.size()>right.size()?left.top():(left.top()+right.top())*0.5;
   }
};
main(){
   MedianClass ob;
   ob.addNum(5);
   ob.addNum(3);
   ob.addNum(8);
   cout << ob.findMedian() << " ";
   ob.addNum(9);
   cout << ob.findMedian() << endl;
}

入力

ob.addNum(5);
ob.addNum(3);
ob.addNum(8);
cout << ob.findMedian() << endl;
ob.addNum(9);
cout << ob.findMedian() << endl;

出力

5.0
6.5

計算量について

この実装では、値の追加(addNum)はヒープへの挿入となるため O(log n)、中央値の取得(findMedian)はヒープの先頭要素を参照するだけなので O(1) で行えます。ストリーム形式で次々と数値が与えられ、その都度中央値を求めたいような場面で非常に有効な手法です。

  1. C++で最小公倍数(LCM)を求めるプログラム:初心者向けに2つの方法を解説

    最小公倍数(LCM: Least Common Multiple)とは、2つの整数に共通する倍数の中で最も小さい数のことです。プログラミングの基礎的なアルゴリズム学習においても頻出のテーマであり、C++を使えば簡単に求めることができます。最小公倍数とは?具体例で確認例として、15と9という2つの数を考えてみましょう。それぞれ素因数分解すると次のようになります。15 = 5 × 3 9 = 3 × 3この場合、15と9の両方を割り切れる最小の数、つまり最小公倍数は 45 となります。方法1:大きい方の数から順に増やしていく方法まず紹介するのは、最も直感的なアプローチです。2つの数のうち大きい方

  2. C++で2つの数の最大公約数(GCD)を求めるプログラム

    最大公約数(GCD)とは最大公約数(GCD: Greatest Common Divisor)とは、2つの整数をどちらも割り切る正の整数のうち、最も大きい数のことです。プログラミングの基礎的なアルゴリズム問題としてよく取り上げられるテーマであり、分数の約分や暗号処理など、さまざまな場面で活用されます。例として、45と27という2つの数を考えてみましょう。45 = 5 × 3 × 327 = 3 × 3 × 3両方の数に共通する素因数は「3 × 3」であるため、45と27の最大公約数は9となります。方法1:ユークリッドの互除法による実装2つの数の最大公約数を求める最も効率的な方法が「ユークリッド