これは 2 つの赤黒ツリーで実行できると思います: 比較関数によって並べ替えられたキーを格納するキー ルックアップ ツリーと、リストのようにキーを任意の順序で並べたインデックス ルックアップ ツリーです。各インデックス ルックアップ ノードには「サイズ」フィールドが必要です。各ノードに「サイズ」フィールドが含まれている場合、赤黒ツリーはインデックスによるルックアップを実行できます。たとえば、C5 Generic Collection Libraryの RedBlackTreeSet 実装を参照してください。
キー ルックアップ ツリーの各エントリには、インデックス ルックアップ ツリー内の対応するエントリへのポインタが必要です。左ノード ポインタと右ノード ポインタと同様に、インデックス ルックアップ ツリーには、ボトム ツーを許可する親ポインタ フィールドが必要です。 -トップ ナビゲーションとトップ ツー ボトム。
全体として、各キーには 6 つのポインターが必要です。両方のノードの通常の左右のポインターに加えて、key-lookup-node から index-lookup-node へのポインター、さらに各 index-lookup の親ポインターです。 -ノード。保存された値を指すために、各ノードにポインターも必要です。
オペレーション:
追加 - 追加操作は、両方のツリーにキーを挿入します。1 回目はキー ルックアップ ツリー、比較関数によって決定された位置、2 回目はインデックス ルックアップ ツリーの右端の位置です。赤黒木への挿入は、対数時間演算です。
キーによるルックアップ - これはキールックアップ ツリーで行われ、比較関数を使用して正しい位置を見つけます - O(log(n))
インデックスによるルックアップ - これは、前述のように、インデックス ルックアップ フィールドで実行できます - O(log(n))
キーからインデックスを取得 - 最初にキー検索ツリー O(log(n)) でキーを検索します。インデックス ルックアップ ツリーまでポインタをたどります。ルート ノード (バランス ツリーの場合は O(log(n))) まで親ポインタをたどります。途中で「サイズ」フィールドを使用して、キーのインデックスを決定します。- O(log(n)) 全体。
インデックスによる削除 - インデックス ルックアップ ツリーでアイテムをルックアップします。インデックス ルックアップ ツリーから削除します。キー検索ツリーで見つかったキーを検索します。キー検索ツリーから削除します。すべての操作は O(log(n)) であるため、削除は全体で O(log(n)) です。
キーで削除 - 「キーからインデックスを取得」を使用して、キーのインデックスを取得します。インデックス ルックアップ ツリーからインデックスごとに削除します。キー検索ツリーからキーごとに削除します。O(log(n)) 全体。
この構造は、最後だけでなく、任意の位置での O(log(n)) 挿入もサポートします。
ストレージのオーバーヘッドは明らかにかなりのものですが、O(n) のままです。時間の複雑さはすべての要件を満たしています。
残念ながら、私はこの構造の実装を認識していません。
更新: ツリーとハッシュ テーブルを組み合わせて、O(1) キーによるルックアップを取得できることがわかりました。上記のように 2 つのツリーを使用する代わりに、キーによる検索にはハッシュ テーブルを使用し、位置による検索にはバランスの取れた順序統計ツリーを使用しますが、ハッシュ テーブルのスロットには次へのポインターを含めます。 get-list-position-by-key 検索を行うためのバランス ツリーのノード。キーによるルックアップは O(1) になり、それ以外は平均して O(ln(n)) のままです。もちろん、他のハッシュ テーブルと同様に、時折 O(n) 再ハッシュ ペナルティが発生します。