イバコの生存記録

いまは競プロ(AtCoder)記事がメインです。

70. ランダムアクセスが O(1) で可能な双方向リスト

双方向リストは、片方向リストと比較して「前後両方のノードへのポインタを持つ」という点が特徴です。C# では LinkedList として実装されています。

f:id:ibako_piyo:20220219131815p:plain

双方向リスト

双方向リストは、以下のような特徴を持ちます。

  • 先頭・末尾への要素追加が  O(1) で可能
  • (そのノードが分かっていれば)ノードの前後への要素追加が  O(1) で可能
  • (そのノードが分かっていれば)ノードの削除が  O(1) で可能
  • 要素の探索が  O(N) で可能

片方向リストと比較して、任意ノード前後への要素追加や、任意ノード削除が高速なのが特徴です。ただし、リスト自体は最初と最後へのポインタしか持っていないため、要素の探索自体は線形時間になります。つまり、定数時間で追加・削除を行うには、対象ノードを知っている必要があります。これは、API として本来隠蔽すべきノードというデータ構造がむき出しになるということです。

 

ただし、制約を設けることで要素の探索を  O(1) で行うことが可能になります。それは、「リスト内に同一(キーを持つ)データが存在しない」という制約です。

これによって、双方向リスト内に「データ値を Key、ノードへのポインタを Value とする Dictionary」を作成することで、全要素へのアクセスを  O(1) で行うことが可能になります。

f:id:ibako_piyo:20220219133337p:plain

辞書付き双方向リスト

このアイデア自体は割とありふれていると思いますが、C# 標準に無く、自分の知る限り特に名前も付いていなかったと思うので、辞書付き双方向リストと呼んでおきます。

辞書付き双方向リストは、以下のような特徴を持ちます。

  • 任意箇所への要素追加が  O(1) で可能
  • 任意要素の削除が  O(1) で可能
  • 要素の探索が  O(1) で可能

このように、リストに対して行うであろうほぼ全ての操作が定数時間で可能になります。加えて、このデータ構造ではノードを完全に隠蔽できます

 

以上のアイデアを実装したものが以下になります。とりあえず IEnumerable<T> だけ実装しましたが、当然ながら要素列挙は  O(N) になります。

gist.github.com

 

 

(アイデアだけを書き留めた昨日の記事↓)

ibako-piyo.hatenablog.com