ウィキペディア 言う:
選択アルゴリズム:最小値、最大値、最小値と最大値の両方、median、またはk番目に大きい要素を見つけるには、次を使用して線形時間で実行できます。ヒープ。
それが言っているのは、それができるのではなく、できるということだけです。
ヒープを使用してこれをどのように行うことができるかについて、いくつかの出発点を教えてもらえますか?
Min-max-medianヒープを使用して、一定時間内にmin、max、およびmedianを見つけます(そして、ヒープを構築するのに線形時間を要します)。順序統計ツリーを使用して、k番目に小さい/大きい値を見つけることができます。これらのデータ構造は両方とも min-maxヒープに関するこのペーパー[pdfリンク] で説明されています。最小最大ヒープは、最小ヒープと最大ヒープを交互に切り替えるバイナリヒープです。
論文から:min-max-medianヒープは、次のプロパティを持つバイナリヒープです。
1)すべての要素の中央値はルートにあります
2)ルートの左サブツリーは、中央値以下の要素を含むサイズceiling [((n-1)/ 2)]の最小最大ヒープHlです。右側のサブツリーは、中央値以上の要素のみを含む、floor [((n-1)/ 2)]サイズの最大最小ヒープHrです。
このホワイトペーパーでは、このようなヒープを構築する方法について説明します。
編集:論文をより徹底的に読むと、min-max-medianヒープを構築するには、まず中央値を見つける必要があるように見えます(FTA:「既知の線形時間アルゴリズムのいずれかを使用して、n個すべての要素の中央値を見つけます」) 。つまり、ヒープを構築したら、左側の最小-最大ヒープと右側の最大-最小ヒープのバランスを維持するだけで、中央値を維持できます。 DeleteMedianは、ルートをmax-minヒープの最小値またはmin-maxヒープの最大値(バランスを維持する方)に置き換えます。
したがって、固定データセットの中央値を見つけるためにmin-max-medianヒープを使用する予定がある場合、SOLですが、変化するデータセットで使用する場合は可能です。
選択アルゴリズム に関するこのウィキペディアのページを参照してください。特に、BFPRTアルゴリズムとMedian of Mediansアルゴリズムを見てください。 BFPRTは確率的に線形であり、クイックソートでモデル化されています。 Median of Mediansは線形であることが保証されていますが、定数係数が大きいため、データセットのサイズによっては実際には時間がかかる場合があります。
中央値を選択する要素が数百または数千しかない場合、簡単なクイックソートとそれに続く直接インデックス作成が最も簡単だと思います。
より良いアルゴリズムが存在する可能性がありますが、私はそれをどのように行うかを以下に示します。
2つのバケットと1つの値があります。値は中央値であり、2つのバケットは「中央値より大きい」と「中央値より小さい」です。配列内の各要素x
について、big_bucket
およびsmall_bucket
サイズの違いは1以下です。アイテムを大きなバケットから小さなバケットに移動する場合、最初に中央値を通過してそこに到達する必要があります(つまり、2の違いは要素をあるバケットから次のバケットに正常にプッシュします-1の違いは要素をプッシュします) 1つのバケットから中央値まで。)配列の最初のパスの終わりに、値は中央値になります。
おそらく元の質問が尋ねられた頃は無かったかもしれませんが、現在ウィキにはソースへのリンクがあり、ここにあります: http://ftp.cs.purdue.edu/research/technical_reports/1991/TR% 2091-027.pdf
具体的には、17ページに進み、RSEL4の説明をご覧ください。それらは定理3.2で、このk番目の選択アルゴリズムの時間計算量がO(k)であることを証明します。したがって、ヒープを構築するにはO(n)を、k番目に小さいアイテムを見つけるには追加のO(k)を必要とします。
他の回答のいくつかが示唆しているほど単純ではない
ヒープデータ構造について詳しく知っていれば、実際にそうであることを簡単に理解できます。ヒープ構造は、O(n) timeで構築できます。最小ヒープと最大ヒープがあります。最小ヒープルート要素は最小要素を提供します。最大ヒープルート要素は最大要素を提供します。ヒープを構築するだけで、中央値と最大k番目の最小値と最大値の同じアイデアを見つけ、ヒープを構築しながら、ツリーの左枝または右枝を見て一定量を維持することで中央値と最大k番目を見つけることができます要素番号などを保存するメモリ.
最初の整数を配列に保存し、カウンターを1に設定します。次に、ベクトル内の残りの整数をループします。配列内の現在の整数が保存されている整数と同じ場合、カウンターは1つ増加し、そうでない場合はカウンターは1つ減少します。カウンターがゼロに達した場合、保存されている整数を破棄し、配列内の現在の整数に置き換えます。最終的にすべての整数をループすると、1つの候補が残ります。次に、配列を再度ループし、候補の出現をカウントして、これが本当に支配的であることを確認する必要があります。
static int FindDominator(int[] arr)
{
int counter = 1;
int candidate = arr[0];
for(int i = 1; i < n; i++)
{
if(arr[i] == candidate) counter++
else
{
counter--;
if(counter == 0) { candidate = arr[i]; counter = 1; }
}
}
counter = 0;
for(int i = 0; i < n; i++)
{
if(arr[i] == candidate) counter++;
}
if(counter > n / 2) return candidate;
else return -1;
}