Closed12

RefCellとRcを使ってスプレー木を実装してみる

powellpowell

設計

フィールド

  • キー,値
  • 親ノードへのポインタ
  • 子ノードへのポインタ(左,右)

ポインタは基本的にOption<Rc<RefCell<Node>>>で持たせたいが,ダングリングポインタが発生してしまうことがあるので,親ノードへのポインタはRcではなくWeakでもっておく.

https://doc.rust-jp.rs/book-ja/ch15-06-reference-cycles.html

struct Node<K, V> {
    key: K,
    value: V,
    parent: Option<Weak<RefCell<Node<K, V>>>>,
    left: Option<Rc<RefCell<Node<K, V>>>>,
    right: Option<Rc<RefCell<Node<K, V>>>>,
}
powellpowell
/// 子方向のポインタ
pub type NodePtr<K, V> = Rc<RefCell<Node<K, V>>>;

/// 親方向のポインタ
pub type ParentPtr<K, V> = Weak<RefCell<Node<K, V>>>;

とエイリアスをかけておくと,

struct Node<K, V> {
    key: K,
    value: V,
    parent: Option<ParentPtr<K, V>>,
    left: Option<NodePtr<K, V>>,
    right: Option<NodePtr<K, V>>,
}

とシンプルに書くことができて便利.

powellpowell

Splayの実装

ボトムアップでスプレーを実装する(対象となるノードを回転で根に上げていく)

状態の取得

ポインタが,「根」「右の子」「左の子」のどれに該当するのかを判定します.
これを実装しておくと,後の回転,スプレーの実装がだいぶ楽になる.

https://github.com/kentakom1213/rust-data-structures/blob/a0d3096c69205511d209c5de24148909a202ceb7/splay_tree/src/node/state.rs#L4-L12

powellpowell

削除

一般に,平衡2分木の削除は実装が煩雑になりがちですが,スプレー木は「検索した値が根に来る」という性質から比較的シンプルに実装できます.

具体的には,スプレー木 T から値 x を削除する場合,

  1. x をもつノード検索し,根に移動させる
  2. Tx 未満の値のみからなる木 T_\mathrm{left}x より大きい値のみからなる木 T_\mathrm{right} に分割する.
  3. T_\mathrm{left}T_\mathrm{right} をマージする

というふうにすれば良いです.

https://github.com/kentakom1213/rust-data-structures/blob/a0d3096c69205511d209c5de24148909a202ceb7/splay_tree/src/node/remove.rs#L16-L42

このスクラップは2ヶ月前にクローズされました