Closed12
RefCellとRcを使ってスプレー木を実装してみる
設計
フィールド
- キー,値
- 親ノードへのポインタ
- 子ノードへのポインタ(左,右)
ポインタは基本的にOption<Rc<RefCell<Node>>>
で持たせたいが,ダングリングポインタが発生してしまうことがあるので,親ノードへのポインタはRc
ではなくWeak
でもっておく.
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>>>>,
}
スプレー木の概要はこちら
/// 子方向のポインタ
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>>,
}
とシンプルに書くことができて便利.
Splayの実装
ボトムアップでスプレーを実装する(対象となるノードを回転で根に上げていく)
状態の取得
ポインタが,「根」「右の子」「左の子」のどれに該当するのかを判定します.
これを実装しておくと,後の回転,スプレーの実装がだいぶ楽になる.
スプレー
回転を準備したので,スプレーは比較的かんたん!
対称な処理はまとめられます.
挿入
値の挿入をします.splitベースの実装もありますが,一般的な「2分探索→葉に挿入」という実装にしました.挿入に関してはこちらでも比較的シンプルに実装できます(削除は大変)
検索
値の検索だけでなく,lower_bound (x以上の最大の値を検索) 等のクエリにも対応したいので,工夫した2分探索で実装します.
比較関数f
を渡し,f
を満たす最小のノードを返す実装にすることでシンプルに実装しました.
削除
一般に,平衡2分木の削除は実装が煩雑になりがちですが,スプレー木は「検索した値が根に来る」という性質から比較的シンプルに実装できます.
具体的には,スプレー木
- 値
をもつノード検索し,根に移動させるx -
をT 未満の値のみからなる木x とT_\mathrm{left} より大きい値のみからなる木x に分割する.T_\mathrm{right} -
とT_\mathrm{left} をマージするT_\mathrm{right}
というふうにすれば良いです.
実行例
こんな感じに動きます.
(可視化は以下のコードで行っています)
コード全体はこんな感じです.
このスクラップは2ヶ月前にクローズされました