Insert、Delete、MostFrequentをサポートする効率的なデータ構造


14

セットあり、各メンバーがデータとキーのペアであると仮定します。次の操作をサポートするデータ構造が必要です。DDD

  • にを挿入し、D(d,k)D
  • メンバー削除します(を見つけるために検索する必要はありません。たとえば、はメンバーを指します)。e e DeeeD
  • MostFrequentは、が最も頻度の高いキーの1つであるようなメンバーを返します(最も頻度の高いキーは一意である必要はありません)。E k e y DeDe.keyD

このデータ構造の効率的な実装は何でしょうか?

私の解決策は、キーとその周波数によって優先順位付けされた周波数のヒープと、ハッシュ関数が同じキーを持つメンバーをハッシュテーブルの同じスロットにマップするハッシュテーブルです(各部分から他へのポインターを使用)。

これにより、最初の2つの操作にが、3番目の操作(最悪の場合の実行時間)にられます。Θ 1 Θ(lgn)Θ(1)

より効率的な解決策があるのだろうか?(または同じ効率のよりシンプルなソリューション?)


必要に応じて、ハッシュテーブルの代わりに単純なバランスの取れたバイナリ検索ツリーを使用できます。
ジョー

ハッシュテーブルは多くの不要なスペースを使用するため、優先キューを提案します。挿入と削除の時間の複雑さは同じですが、メモリの複雑さが向上します。
バルトシュプルジビルスキ

@Joe、ハッシュテーブルの代わりにBSTを使用すると、MostFrequent操作の効率が低下しますが、これはメモリの合理的なトレードオフになる可能性があります。
カベ

2
比較のみを使用する場合、要素の明確性の問題の下限のため、Insert / MostFrequentの少なくとも1つをΩログnで償却する必要があります
アルヤバタ

1
ストリーミングモデルには興味深い構造もいくつかあります。springerlink.com/content/t17nhd9hwwry909p
ジョー

回答:


7

比較ベースの計算モデルでは、通常のヒープの代わりにフィボナッチヒープを使用して優先度キューを実装できます。これにより、 挿入の償却時間とOlog n )の削除操作の償却時間の境界が与えられます。O1Oログn

比較ベースのモデルから離れ、キーがバイナリ文字列とみなされるRAMモデルを採用する場合、それぞれが1つ以上のマシンワードに含まれる場合、優先キューを実装できます。確かに、挿入操作と削除操作の両方で達成できますOo(logn)およびfindMin操作のO1時間。ソープはそれを証明しましたO(loglogn)O(1)

キーごとに時間S n キーをソートできる場合、一定時間でfind-minをサポートし、S n 時間で更新(挿入および削除)をサポートする優先度キューを実装できます。nS(n)S(n)

M. Thorupを参照してください。優先度キューとソートの等価性、2002年。Proc。FOCS 2002

我々はに並べ替えることができますので、示される予想時間と線形空間O(nloglogn

Y.ハンとM.ソーラップ。整数でソート予想される時間と線形空間。Procで。FOCS 2002Onログログn

限界が証明されています。


1

予想償却時間でこれらすべてを実行できます。重要なトリックは、優先度キューの全能力を必要としないことです。なぜなら、キーの頻度は、挿入または削除のたびに1だけ変化するからです。O1

私の以下のソリューションは、実際にはこのケースでうまく機能する「非効率的な」優先度キューを備えたソリューションです。キーのバケットの二重リンクリストとして実装された最大優先度キューは、O(1) incrementKey。


バケットの二重リンクリストを保持します。各バケットには、空ではないキーのハッシュセット(コホートと呼びます)と正の整数(私はValCountと呼びます)があります。バケットbでは、bのコホート内の各キーkには、保持しているセット内のそれに関連付けられた同じ数の一意の値があります。たとえば、セットにペア(a、apple)、(a、avocado)、(b、banana)、(c、cucumber)、(d、dragon fruit)があり、1文字がキーで、果物が値の場合、2つのバケットがあります。1つのバケットには2のValCountと、1つのキーのみで構成されるコホートがあります。もう1つのバケットのValCountは1で、3つのキーb、c、およびdで構成されるコホートがあります。

バケットの二重リンクリストは、ValCountによって順序付けられたままにする必要があります。時間でリストの先頭と末尾を見つけることができ、その隣人がわかっている場合はO 1 時間で新しいバケットに接続できることが重要です。想像を絶するように、バケットのリストをBucketListと呼びます。O1O1

BucketListに加えて、SetMapが必要です。これは、キーをValueBucketsにマッピングするハッシュマップです。ValueBucketは、ValueSet(値の空でないハッシュセット)とバケットへのnull以外のポインターで構成されるペアです。キーkに関連付けられたValueSetには、kに関連付けられたすべての一意の値が含まれます。ValueSetに関連付けられたバケットポインターには、ValueSetのサイズに等しいコホートがあります。SetMapのキーkに関連付けられているBucketは、BucketListのキーkにも関連付けられています。

C ++の場合:

struct Bucket {
    unsigned ValCount;
    unordered_set<Key> Cohort;
    Bucket * heavier;
    Bucket * lighter;
};
Bucket * BucketListHead;
Bucket * BucketListTail;

struct ValueBucket {
  unordered_set<Value> ValueSet;
  Bucket * bucket;
};
unordered_map<Key, ValueBucket> SetMap;

最大頻度のキーと値のペアを見つけるには、BucketListの先頭を見て、Cohortでキーを見つけ、SetMapでそのキーを探し、ValueBucketのValueSetで値を見つけるだけです。(ふう!)

キーと値のペアの挿入と削除はより複雑です。

キーと値のペアを挿入または削除するには、まずSetMapに挿入または削除します。これにより、ValueSetのサイズが変更されるため、キーに関連付けられたバケットを変更する必要があります。この変更を行うために見る必要があるバケットは、キーが使用されていたバケットのすぐ隣になります。ここにはいくつかのケースがあり、完全に説明する価値はありません。まだ問題がある場合は詳しく説明します。


ありがとう。実際には、解決策についても同様のアイデアがありましたが、それは問題でした。今は思い出せないので、何が問題なのかを確認する必要があります。来週、これをもっと注意深くチェックして、ソリューションが抱えていた問題を回避できるかどうかを確認します。
カヴェー

私の解決策を考える別の方法は次のとおりです。このケースでうまく機能するのは、たまたま「非効率的な」優先度キューを備えたあなたの解決策です。キーのバケットの二重リンクリストとして実装された最大優先度キューには、O(1)insertMin、deleteMax、removeFromBucket、increaseKeyがあります。
jbapple

ValueBucketsのキーからのマッピングを維持する最も効率的な方法(最悪の場合)は、おそらく検索ツリーです。
ラファエル

ラファエル-あなたが何を得ているのか分かりません。ハッシュテーブルは実際には高価であると言っていますか?
jbapple

-3

最悪の複雑さ

O1

O1

O1

Oログログn

On

証明

[0N Olog log mn{nN}時間。

次の組み合わせで確立されます。

τnN n[0N τnNτNNτ

そして:

n[0NO1+log log nlog log qq

参照

ソラップ、ミケル。「一定時間でキーを減らす整数優先度キューと単一ソースの最短パス問題」。第35回コンピューティング理論に関するACMシンポジウムの議事録、149–158。STOC '03。ニューヨーク、ニューヨーク、アメリカ:ACM、2003。


すべての優先度キューで、「get-min」および「extract-min」をサポートする構造から「get-max」および「extract-max」をサポートする構造に移動するのは簡単です。
AT

Pingの...:@Kaveh
AT
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.