_std::set
_はソートされたツリーです。これはbegin
およびend
メソッドを提供するので、最小値と最大値、およびバイナリ検索用の_lower_bound
_と_upper_bound
_を取得できます。しかし、イテレータが中央の要素(または要素が偶数の場合はそのうちの1つ)を指すようにするにはどうすればよいですか?
それを行うための効率的な方法はありますか(O(log(size))
ではなくO(size)
)?
_{1} => 1
{1,2} => 1 or 2
{1,2,3} => 2
{1,2,3,4} => 2 or 3 (but in the same direction from middle as for {1,2})
{1,312,10000,14000,152333} => 10000
_
PS: ロシア語で同じ質問です
アイテムを挿入/削除する頻度と中央/中央値を検索する頻度に応じて、明白なソリューションよりもおそらくより効率的なソリューションは、中間の要素への永続イテレータを保持し、セットにアイテムを挿入/削除するたびにそれを更新することです。処理が必要なEdgeケースは多数あります(奇数対偶数のアイテム、中間アイテムの削除、空のセットなど)。ただし、基本的な考え方は、現在の中間アイテムよりも小さいアイテムを挿入すると、 、中間のイテレータはデクリメントが必要な場合がありますが、大きなイテレータを挿入する場合は、インクリメントする必要があります。これは、削除の別の方法です。
ルックアップ時には、これはもちろんO(1)ですが、本質的にO(1)挿入/削除ごとのコスト、つまりO(N) N挿入後。ブルートフォースよりも効率的にするには、十分な数のルックアップにわたって償却する必要があります。
O(size))になり、バイナリ検索ツリーの真ん中を取得します。次のようにstd::advance()
を使用して取得できます。
std::set<int>::iterator it = s.begin();
std::advance(it, s.size() / 2);
アイテムを挿入/削除する頻度と中央/中央値を検索する頻度に応じて、明白なソリューションよりもおそらくより効率的なソリューションは、中間の要素への永続イテレータを保持し、セットにアイテムを挿入/削除するたびにそれを更新することです。処理が必要なEdgeケースは多数あります(奇数対偶数のアイテム、中間アイテムの削除、空のセットなど)。ただし、基本的な考え方は、現在の中間アイテムよりも小さいアイテムを挿入すると、 、中間のイテレータはデクリメントが必要な場合がありますが、大きなイテレータを挿入する場合は、インクリメントする必要があります。これは、削除の別の方法です。
void balance(multiset<int> &small, multiset<int> &big)
{
while (true)
{
int ssmall = small.size();
int sbig = big.size();
if (ssmall == sbig || ssmall + 1 == sbig) break; // OK
if (ssmall < sbig)
{
// big to small
auto v = big.begin();
small.emplace(*v);
big.erase(v);
}
else
{
// small to big
auto v = small.end();
--v;
big.emplace(*v);
small.erase(v);
}
}
}
auto medium = big.begin();
cout << *medium << endl;
auto v = big.begin();
if (v != big.end() && new_item > *v)
big.emplace(new_item );
else
small.emplace(new_item );
balance(small, big);
std::set
は重複した値を格納しないことに注意してください。次の値{1, 2, 3, 3, 3, 3, 3, 3, 3}
を挿入すると、取得する中央値は2
になります。
std::set<int>::iterator it = s.begin();
std::advance(it, s.size() / 2);
int median = *it;
中央値を考慮するときに重複を含めたい場合は、std::multiset
を使用できます({1, 2, 3, 3, 3, 3, 3, 3, 3}
中央値は3
になります):
std::multiset<int>::iterator it = s.begin();
std::advance(it, s.size() / 2);
int median = *it;
データを並べ替える唯一の理由が中央値を取得することである場合、私の考えでは単純な古いstd::vector
+ std::sort
を使用するほうがよいでしょう。
大きなテストサンプルと複数の反復を使用して、std::vector
とstd::sort
で5秒、std::set
またはstd::multiset
で13〜15秒でテストを完了しました。あなたの走行距離は、あなたが持っている重複する値のサイズと数によって異なります。