予想償却時間でこれらすべてを実行できます。重要なトリックは、優先度キューの全能力を必要としないことです。なぜなら、キーの頻度は、挿入または削除のたびに1だけ変化するからです。O (1 )
私の以下のソリューションは、実際にはこのケースでうまく機能する「非効率的な」優先度キューを備えたソリューションです。キーのバケットの二重リンクリストとして実装された最大優先度キューは、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と呼びます。O (1 )O (1 )
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のサイズが変更されるため、キーに関連付けられたバケットを変更する必要があります。この変更を行うために見る必要があるバケットは、キーが使用されていたバケットのすぐ隣になります。ここにはいくつかのケースがあり、完全に説明する価値はありません。まだ問題がある場合は詳しく説明します。